blob: b01a6fbf1ce67487fe812849d5283f89b15ff8aa [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.
*
*************************************************************/
#include "oox/xls/externallinkbuffer.hxx"
#include <com/sun/star/sheet/ComplexReference.hpp>
#include <com/sun/star/sheet/DDELinkInfo.hpp>
#include <com/sun/star/sheet/ExternalLinkType.hpp>
#include <com/sun/star/sheet/ExternalReference.hpp>
#include <com/sun/star/sheet/ReferenceFlags.hpp>
#include <com/sun/star/sheet/SingleReference.hpp>
#include <com/sun/star/sheet/XDDELinks.hpp>
#include <com/sun/star/sheet/XDDELink.hpp>
#include <com/sun/star/sheet/XDDELinkResults.hpp>
#include <com/sun/star/sheet/XExternalDocLink.hpp>
#include <com/sun/star/sheet/XExternalDocLinks.hpp>
#include <rtl/strbuf.hxx>
#include "oox/core/filterbase.hxx"
#include "oox/helper/attributelist.hxx"
#include "oox/xls/addressconverter.hxx"
#include "oox/xls/biffinputstream.hxx"
#include "oox/xls/excelhandlers.hxx"
#include "oox/xls/formulaparser.hxx"
#include "oox/xls/worksheetbuffer.hxx"
namespace oox {
namespace xls {
// ============================================================================
using namespace ::com::sun::star::sheet;
using namespace ::com::sun::star::table;
using namespace ::com::sun::star::uno;
using ::oox::core::Relation;
using ::oox::core::Relations;
using ::rtl::OString;
using ::rtl::OStringBuffer;
using ::rtl::OStringToOUString;
using ::rtl::OUString;
// ============================================================================
namespace {
const sal_uInt16 BIFF12_EXTERNALBOOK_BOOK = 0;
const sal_uInt16 BIFF12_EXTERNALBOOK_DDE = 1;
const sal_uInt16 BIFF12_EXTERNALBOOK_OLE = 2;
const sal_uInt16 BIFF12_EXTNAME_AUTOMATIC = 0x0002;
const sal_uInt16 BIFF12_EXTNAME_PREFERPIC = 0x0004;
const sal_uInt16 BIFF12_EXTNAME_STDDOCNAME = 0x0008;
const sal_uInt16 BIFF12_EXTNAME_OLEOBJECT = 0x0010;
const sal_uInt16 BIFF12_EXTNAME_ICONIFIED = 0x0020;
const sal_uInt16 BIFF_EXTNAME_BUILTIN = 0x0001;
const sal_uInt16 BIFF_EXTNAME_AUTOMATIC = 0x0002;
const sal_uInt16 BIFF_EXTNAME_PREFERPIC = 0x0004;
const sal_uInt16 BIFF_EXTNAME_STDDOCNAME = 0x0008;
const sal_uInt16 BIFF_EXTNAME_OLEOBJECT = 0x0010;
const sal_uInt16 BIFF_EXTNAME_ICONIFIED = 0x8000;
} // namespace
// ============================================================================
ExternalNameModel::ExternalNameModel() :
mbBuiltIn( false ),
mbNotify( false ),
mbPreferPic( false ),
mbStdDocName( false ),
mbOleObj( false ),
mbIconified( false )
{
}
// ============================================================================
ExternalName::ExternalName( const ExternalLink& rParentLink ) :
DefinedNameBase( rParentLink ),
mrParentLink( rParentLink ),
mnStorageId( 0 ),
mbDdeLinkCreated( false )
{
}
void ExternalName::importDefinedName( const AttributeList& rAttribs )
{
maModel.maName = rAttribs.getXString( XML_name, OUString() );
OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importDefinedName - empty name" );
// zero-based index into sheet list of externalBook
maModel.mnSheet = rAttribs.getInteger( XML_sheetId, -1 );
}
void ExternalName::importDdeItem( const AttributeList& rAttribs )
{
maModel.maName = rAttribs.getXString( XML_name, OUString() );
OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importDdeItem - empty name" );
maExtNameModel.mbOleObj = false;
maExtNameModel.mbStdDocName = rAttribs.getBool( XML_ole, false );
maExtNameModel.mbNotify = rAttribs.getBool( XML_advise, false );
maExtNameModel.mbPreferPic = rAttribs.getBool( XML_preferPic, false );
}
void ExternalName::importValues( const AttributeList& rAttribs )
{
setResultSize( rAttribs.getInteger( XML_cols, 1 ), rAttribs.getInteger( XML_rows, 1 ) );
}
void ExternalName::importOleItem( const AttributeList& rAttribs )
{
maModel.maName = rAttribs.getXString( XML_name, OUString() );
OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importOleItem - empty name" );
maExtNameModel.mbOleObj = true;
maExtNameModel.mbNotify = rAttribs.getBool( XML_advise, false );
maExtNameModel.mbPreferPic = rAttribs.getBool( XML_preferPic, false );
maExtNameModel.mbIconified = rAttribs.getBool( XML_icon, false );
}
void ExternalName::importExternalName( SequenceInputStream& rStrm )
{
rStrm >> maModel.maName;
OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importExternalName - empty name" );
}
void ExternalName::importExternalNameFlags( SequenceInputStream& rStrm )
{
sal_uInt16 nFlags;
sal_Int32 nSheetId;
rStrm >> nFlags >> nSheetId;
// index into sheet list of EXTSHEETNAMES (one-based in BIFF12)
maModel.mnSheet = nSheetId - 1;
// no flag for built-in names, as in OOXML...
maExtNameModel.mbNotify = getFlag( nFlags, BIFF12_EXTNAME_AUTOMATIC );
maExtNameModel.mbPreferPic = getFlag( nFlags, BIFF12_EXTNAME_PREFERPIC );
maExtNameModel.mbStdDocName = getFlag( nFlags, BIFF12_EXTNAME_STDDOCNAME );
maExtNameModel.mbOleObj = getFlag( nFlags, BIFF12_EXTNAME_OLEOBJECT );
maExtNameModel.mbIconified = getFlag( nFlags, BIFF12_EXTNAME_ICONIFIED );
OSL_ENSURE( (mrParentLink.getLinkType() == LINKTYPE_OLE) == maExtNameModel.mbOleObj,
"ExternalName::importExternalNameFlags - wrong OLE flag in external name" );
}
void ExternalName::importDdeItemValues( SequenceInputStream& rStrm )
{
sal_Int32 nRows, nCols;
rStrm >> nRows >> nCols;
setResultSize( nCols, nRows );
}
void ExternalName::importDdeItemBool( SequenceInputStream& rStrm )
{
appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 );
}
void ExternalName::importDdeItemDouble( SequenceInputStream& rStrm )
{
appendResultValue( rStrm.readDouble() );
}
void ExternalName::importDdeItemError( SequenceInputStream& rStrm )
{
appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) );
}
void ExternalName::importDdeItemString( SequenceInputStream& rStrm )
{
appendResultValue( BiffHelper::readString( rStrm ) );
}
void ExternalName::importExternalName( BiffInputStream& rStrm )
{
sal_uInt16 nFlags = 0;
if( getBiff() >= BIFF3 )
{
rStrm >> nFlags;
maExtNameModel.mbBuiltIn = getFlag( nFlags, BIFF_EXTNAME_BUILTIN );
maExtNameModel.mbNotify = getFlag( nFlags, BIFF_EXTNAME_AUTOMATIC );
maExtNameModel.mbPreferPic = getFlag( nFlags, BIFF_EXTNAME_PREFERPIC );
// BIFF5-BIFF8: sheet index for sheet-local names, OLE settings
if( getBiff() >= BIFF5 )
{
maExtNameModel.mbStdDocName = getFlag( nFlags, BIFF_EXTNAME_STDDOCNAME );
maExtNameModel.mbOleObj = getFlag( nFlags, BIFF_EXTNAME_OLEOBJECT );
maExtNameModel.mbIconified = getFlag( nFlags, BIFF_EXTNAME_ICONIFIED );
if( maExtNameModel.mbOleObj )
{
rStrm >> mnStorageId;
}
else
{
/* Import the reference ID for names that are sheet-local in
the external document. This index will be resolved later to
the index of the external sheet cache which is able to
provide the name of the sheet related to this defined name.
- BIFF5: one-based index to EXTERNSHEET record containing
the document and sheet name
- BIFF8: one-based index into EXTERNALBOOK sheet name list
The value zero means this external name is a global name.
*/
rStrm.skip( 2 );
maModel.mnSheet = rStrm.readuInt16();
}
}
}
maModel.maName = (getBiff() == BIFF8) ?
rStrm.readUniStringBody( rStrm.readuInt8() ) :
rStrm.readByteStringUC( false, getTextEncoding() );
OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importExternalName - empty name" );
// load cell references that are stored in hidden external names (seen in BIFF3-BIFF4)
bool bHiddenRef = (getBiff() <= BIFF4) && (maModel.maName.getLength() > 1) && (maModel.maName[ 0 ] == '\x01') && (rStrm.getRemaining() > 2);
switch( mrParentLink.getLinkType() )
{
case LINKTYPE_INTERNAL:
// cell references to other internal sheets are stored in hidden external names
if( bHiddenRef && (getBiff() == BIFF4) && isWorkbookFile() )
{
ApiTokenSequence aTokens = importBiffFormula( mrParentLink.getCalcSheetIndex(), rStrm );
extractReference( aTokens );
}
break;
case LINKTYPE_EXTERNAL:
// cell references to other documents are stored in hidden external names
if( bHiddenRef )
{
ApiTokenSequence aTokens = importBiffFormula( 0, rStrm );
extractExternalReference( aTokens );
}
break;
case LINKTYPE_DDE:
case LINKTYPE_OLE:
case LINKTYPE_MAYBE_DDE_OLE:
// DDE/OLE link results
if( rStrm.getRemaining() > 3 )
{
bool bBiff8 = getBiff() == BIFF8;
sal_Int32 nCols = rStrm.readuInt8();
sal_Int32 nRows = rStrm.readuInt16();
if( bBiff8 ) { ++nCols; ++nRows; } else if( nCols == 0 ) nCols = 256;
setResultSize( nCols, nRows );
bool bLoop = true;
while( bLoop && !rStrm.isEof() && (maCurrIt != maResults.end()) )
{
switch( rStrm.readuInt8() )
{
case BIFF_DATATYPE_EMPTY:
appendResultValue( OUString() );
rStrm.skip( 8 );
break;
case BIFF_DATATYPE_DOUBLE:
appendResultValue( rStrm.readDouble() );
break;
case BIFF_DATATYPE_STRING:
appendResultValue( bBiff8 ? rStrm.readUniString() : rStrm.readByteStringUC( false, getTextEncoding() ) );
break;
case BIFF_DATATYPE_BOOL:
appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 );
rStrm.skip( 7 );
break;
case BIFF_DATATYPE_ERROR:
appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) );
rStrm.skip( 7 );
break;
default:
bLoop = false;
}
}
OSL_ENSURE( bLoop && !rStrm.isEof() && (maCurrIt == maResults.end()),
"ExternalName::importExternalName - stream error in result set" );
}
break;
default:;
}
}
#if 0
sal_Int32 ExternalName::getSheetCacheIndex() const
{
OSL_ENSURE( mrParentLink.getLinkType() == LINKTYPE_DDE, "ExternalName::getSheetCacheIndex - unexpected link type" );
sal_Int32 nCacheIdx = -1;
switch( getFilterType() )
{
case FILTER_OOXML:
// OOXML/BIFF12: zero-based index into sheet list, -1 means global name
if( maModel.mnSheet >= 0 )
nCacheIdx = mrParentLink.getSheetIndex( maModel.mnSheet );
break;
case FILTER_BIFF:
switch( getBiff() )
{
case BIFF2:
case BIFF3:
case BIFF4:
break;
case BIFF5:
if( maModel.mnSheet > 0 )
if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( maModel.mnSheet ).get() )
if( pExtLink->getLinkType() == LINKTYPE_EXTERNAL )
nCacheIdx = pExtLink->getSheetIndex();
break;
case BIFF8:
if( maModel.mnSheet > 0 )
nCacheIdx = mrParentLink.getSheetIndex( maModel.mnSheet - 1 );
break;
case BIFF_UNKNOWN:
break;
}
break;
case FILTER_UNKNOWN:
break;
}
return nCacheIdx;
}
#endif
bool ExternalName::getDdeItemInfo( DDEItemInfo& orItemInfo ) const
{
if( (mrParentLink.getLinkType() == LINKTYPE_DDE) && (maModel.maName.getLength() > 0) )
{
orItemInfo.Item = maModel.maName;
orItemInfo.Results = ContainerHelper::matrixToSequenceSequence( maResults );
return true;
}
return false;
}
bool ExternalName::getDdeLinkData( OUString& orDdeServer, OUString& orDdeTopic, OUString& orDdeItem )
{
if( (mrParentLink.getLinkType() == LINKTYPE_DDE) && (maModel.maName.getLength() > 0) )
{
// try to create a DDE link and to set the imported link results
if( !mbDdeLinkCreated ) try
{
PropertySet aDocProps( getDocument() );
Reference< XDDELinks > xDdeLinks( aDocProps.getAnyProperty( PROP_DDELinks ), UNO_QUERY_THROW );
mxDdeLink = xDdeLinks->addDDELink( mrParentLink.getClassName(), mrParentLink.getTargetUrl(), maModel.maName, ::com::sun::star::sheet::DDELinkMode_DEFAULT );
mbDdeLinkCreated = true; // ignore if setting results fails
if( !maResults.empty() )
{
Reference< XDDELinkResults > xResults( mxDdeLink, UNO_QUERY_THROW );
xResults->setResults( ContainerHelper::matrixToSequenceSequence( maResults ) );
}
}
catch( Exception& )
{
OSL_ENSURE( false, "ExternalName::getDdeLinkData - cannot create DDE link" );
}
// get link data from created DDE link
if( mxDdeLink.is() )
{
orDdeServer = mxDdeLink->getApplication();
orDdeTopic = mxDdeLink->getTopic();
orDdeItem = mxDdeLink->getItem();
return true;
}
}
return false;
}
// private --------------------------------------------------------------------
namespace {
void lclSetSheetCacheIndex( SingleReference& orApiRef, sal_Int32 nCacheIdx )
{
using namespace ::com::sun::star::sheet::ReferenceFlags;
setFlag( orApiRef.Flags, SHEET_RELATIVE, false );
setFlag( orApiRef.Flags, SHEET_3D, true );
orApiRef.Sheet = nCacheIdx;
}
} // namespace
void ExternalName::extractExternalReference( const ApiTokenSequence& rTokens )
{
OSL_ENSURE( (getFilterType() == FILTER_BIFF) && (getBiff() <= BIFF4), "ExternalName::setExternalReference - unexpected call" );
sal_Int32 nDocLinkIdx = mrParentLink.getDocumentLinkIndex();
sal_Int32 nCacheIdx = mrParentLink.getSheetCacheIndex();
if( (nDocLinkIdx >= 0) && (nCacheIdx >= 0) )
{
ExternalReference aExtApiRef;
aExtApiRef.Index = nDocLinkIdx;
Any aRefAny = getFormulaParser().extractReference( rTokens );
if( aRefAny.has< SingleReference >() )
{
SingleReference aApiRef;
aRefAny >>= aApiRef;
lclSetSheetCacheIndex( aApiRef, nCacheIdx );
aExtApiRef.Reference <<= aApiRef;
maRefAny <<= aExtApiRef;
}
else if( aRefAny.has< ComplexReference >() )
{
ComplexReference aApiRef;
aRefAny >>= aApiRef;
lclSetSheetCacheIndex( aApiRef.Reference1, nCacheIdx );
lclSetSheetCacheIndex( aApiRef.Reference2, nCacheIdx );
aExtApiRef.Reference <<= aApiRef;
maRefAny <<= aExtApiRef;
}
}
}
void ExternalName::setResultSize( sal_Int32 nColumns, sal_Int32 nRows )
{
OSL_ENSURE( (mrParentLink.getLinkType() == LINKTYPE_DDE) || (mrParentLink.getLinkType() == LINKTYPE_OLE) ||
(mrParentLink.getLinkType() == LINKTYPE_MAYBE_DDE_OLE), "ExternalName::setResultSize - wrong link type" );
OSL_ENSURE( (nRows > 0) && (nColumns > 0), "ExternalName::setResultSize - invalid matrix size" );
const CellAddress& rMaxPos = getAddressConverter().getMaxApiAddress();
if( (0 < nRows) && (nRows <= rMaxPos.Row + 1) && (0 < nColumns) && (nColumns <= rMaxPos.Column + 1) )
maResults.resize( static_cast< size_t >( nColumns ), static_cast< size_t >( nRows ), Any( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) ) );
else
maResults.clear();
maCurrIt = maResults.begin();
}
// ============================================================================
void LinkSheetRange::setDeleted()
{
meType = LINKSHEETRANGE_INTERNAL;
mnDocLink = mnFirst = mnLast = -1;
}
void LinkSheetRange::setSameSheet()
{
meType = LINKSHEETRANGE_SAMESHEET;
mnDocLink = -1;
mnFirst = mnLast = 0;
}
void LinkSheetRange::setRange( sal_Int32 nFirst, sal_Int32 nLast )
{
meType = LINKSHEETRANGE_INTERNAL;
mnDocLink = -1;
mnFirst = ::std::min( nFirst, nLast );
mnLast = ::std::max( nFirst, nLast );
}
void LinkSheetRange::setExternalRange( sal_Int32 nDocLink, sal_Int32 nFirst, sal_Int32 nLast )
{
if( nDocLink < 0 )
{
setDeleted();
}
else
{
meType = LINKSHEETRANGE_EXTERNAL;
mnDocLink = nDocLink;
mnFirst = ::std::min( nFirst, nLast );
mnLast = ::std::max( nFirst, nLast );
}
}
// ============================================================================
ExternalLink::ExternalLink( const WorkbookHelper& rHelper ) :
WorkbookHelper( rHelper ),
meLinkType( LINKTYPE_UNKNOWN ),
meFuncLibType( FUNCLIB_UNKNOWN )
{
}
void ExternalLink::importExternalReference( const AttributeList& rAttribs )
{
maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
}
void ExternalLink::importExternalBook( const Relations& rRelations, const AttributeList& rAttribs )
{
parseExternalReference( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
}
void ExternalLink::importSheetName( const AttributeList& rAttribs )
{
insertExternalSheet( rAttribs.getXString( XML_val, OUString() ) );
}
void ExternalLink::importDefinedName( const AttributeList& rAttribs )
{
createExternalName()->importDefinedName( rAttribs );
}
void ExternalLink::importDdeLink( const AttributeList& rAttribs )
{
OUString aDdeService = rAttribs.getXString( XML_ddeService, OUString() );
OUString aDdeTopic = rAttribs.getXString( XML_ddeTopic, OUString() );
setDdeOleTargetUrl( aDdeService, aDdeTopic, LINKTYPE_DDE );
}
ExternalNameRef ExternalLink::importDdeItem( const AttributeList& rAttribs )
{
ExternalNameRef xExtName = createExternalName();
xExtName->importDdeItem( rAttribs );
return xExtName;
}
void ExternalLink::importOleLink( const Relations& rRelations, const AttributeList& rAttribs )
{
OUString aProgId = rAttribs.getXString( XML_progId, OUString() );
OUString aTargetUrl = rRelations.getExternalTargetFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
setDdeOleTargetUrl( aProgId, aTargetUrl, LINKTYPE_OLE );
}
ExternalNameRef ExternalLink::importOleItem( const AttributeList& rAttribs )
{
ExternalNameRef xExtName = createExternalName();
xExtName->importOleItem( rAttribs );
return xExtName;
}
void ExternalLink::importExternalRef( SequenceInputStream& rStrm )
{
rStrm >> maRelId;
}
void ExternalLink::importExternalSelf( SequenceInputStream& )
{
meLinkType = LINKTYPE_SELF;
}
void ExternalLink::importExternalSame( SequenceInputStream& )
{
meLinkType = LINKTYPE_SAME;
}
void ExternalLink::importExternalAddin( SequenceInputStream& )
{
meLinkType = LINKTYPE_UNKNOWN;
}
void ExternalLink::importExternalBook( const Relations& rRelations, SequenceInputStream& rStrm )
{
switch( rStrm.readuInt16() )
{
case BIFF12_EXTERNALBOOK_BOOK:
parseExternalReference( rRelations, BiffHelper::readString( rStrm ) );
break;
case BIFF12_EXTERNALBOOK_DDE:
{
OUString aDdeService, aDdeTopic;
rStrm >> aDdeService >> aDdeTopic;
setDdeOleTargetUrl( aDdeService, aDdeTopic, LINKTYPE_DDE );
}
break;
case BIFF12_EXTERNALBOOK_OLE:
{
OUString aTargetUrl = rRelations.getExternalTargetFromRelId( BiffHelper::readString( rStrm ) );
OUString aProgId = BiffHelper::readString( rStrm );
setDdeOleTargetUrl( aProgId, aTargetUrl, LINKTYPE_OLE );
}
break;
default:
OSL_ENSURE( false, "ExternalLink::importExternalBook - unknown link type" );
}
}
void ExternalLink::importExtSheetNames( SequenceInputStream& rStrm )
{
// load external sheet names and create the sheet caches in the Calc document
OSL_ENSURE( (meLinkType == LINKTYPE_EXTERNAL) || (meLinkType == LINKTYPE_LIBRARY),
"ExternalLink::importExtSheetNames - invalid link type" );
if( meLinkType == LINKTYPE_EXTERNAL ) // ignore sheets of external libraries
for( sal_Int32 nSheet = 0, nCount = rStrm.readInt32(); !rStrm.isEof() && (nSheet < nCount); ++nSheet )
insertExternalSheet( BiffHelper::readString( rStrm ) );
}
ExternalNameRef ExternalLink::importExternalName( SequenceInputStream& rStrm )
{
ExternalNameRef xExtName = createExternalName();
xExtName->importExternalName( rStrm );
return xExtName;
}
void ExternalLink::importExternSheet( BiffInputStream& rStrm )
{
OStringBuffer aTargetBuffer( rStrm.readByteString( false, true ) );
// references to own sheets have wrong string length field (off by 1)
if( (aTargetBuffer.getLength() > 0) && (aTargetBuffer[ 0 ] == 3) )
aTargetBuffer.append( static_cast< sal_Char >( rStrm.readuInt8() ) );
// parse the encoded URL
OUString aBiffTarget = OStringToOUString( aTargetBuffer.makeStringAndClear(), getTextEncoding() );
OUString aSheetName = parseBiffTargetUrl( aBiffTarget );
switch( meLinkType )
{
case LINKTYPE_INTERNAL:
maCalcSheets.push_back( getWorksheets().getCalcSheetIndex( aSheetName ) );
break;
case LINKTYPE_EXTERNAL:
insertExternalSheet( (aSheetName.getLength() > 0) ? aSheetName : WorksheetBuffer::getBaseFileName( maTargetUrl ) );
break;
default:;
}
}
void ExternalLink::importExternalBook( BiffInputStream& rStrm )
{
OUString aTarget;
sal_uInt16 nSheetCount;
rStrm >> nSheetCount;
if( rStrm.getRemaining() == 2 )
{
if( rStrm.readuInt8() == 1 )
{
sal_Char cChar = static_cast< sal_Char >( rStrm.readuInt8() );
if( cChar != 0 )
aTarget = OStringToOUString( OString( cChar ), getTextEncoding() );
}
}
else if( rStrm.getRemaining() >= 3 )
{
// NUL characters may occur
aTarget = rStrm.readUniString( true );
}
// parse the encoded URL
OUString aDummySheetName = parseBiffTargetUrl( aTarget );
OSL_ENSURE( aDummySheetName.getLength() == 0, "ExternalLink::importExternalBook - sheet name in encoded URL" );
(void)aDummySheetName; // prevent compiler warning
// load external sheet names and create the sheet caches in the Calc document
if( meLinkType == LINKTYPE_EXTERNAL )
for( sal_uInt16 nSheet = 0; !rStrm.isEof() && (nSheet < nSheetCount); ++nSheet )
insertExternalSheet( rStrm.readUniString() );
}
void ExternalLink::importExternalName( BiffInputStream& rStrm )
{
ExternalNameRef xExtName = createExternalName();
xExtName->importExternalName( rStrm );
switch( meLinkType )
{
case LINKTYPE_DDE:
OSL_ENSURE( !xExtName->isOleObject(), "ExternalLink::importExternalName - OLE object in DDE link" );
break;
case LINKTYPE_OLE:
OSL_ENSURE( xExtName->isOleObject(), "ExternalLink::importExternalName - anything but OLE object in OLE link" );
break;
case LINKTYPE_MAYBE_DDE_OLE:
meLinkType = xExtName->isOleObject() ? LINKTYPE_OLE : LINKTYPE_DDE;
break;
default:
OSL_ENSURE( !xExtName->isOleObject(), "ExternalLink::importExternalName - OLE object in external name" );
}
}
ExternalLinkInfo ExternalLink::getLinkInfo() const
{
ExternalLinkInfo aLinkInfo;
switch( meLinkType )
{
case LINKTYPE_SELF:
case LINKTYPE_SAME:
case LINKTYPE_INTERNAL:
aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::SELF;
break;
case LINKTYPE_EXTERNAL:
aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::DOCUMENT;
aLinkInfo.Data <<= maTargetUrl;
break;
case LINKTYPE_LIBRARY:
// parser will return library function names in OPCODE_BAD string tokens
aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::SPECIAL;
break;
case LINKTYPE_DDE:
{
aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::DDE;
DDELinkInfo aDdeLinkInfo;
aDdeLinkInfo.Service = maClassName;
aDdeLinkInfo.Topic = maTargetUrl;
::std::vector< DDEItemInfo > aItemInfos;
DDEItemInfo aItemInfo;
for( ExternalNameVector::const_iterator aIt = maExtNames.begin(), aEnd = maExtNames.end(); aIt != aEnd; ++aIt )
if( (*aIt)->getDdeItemInfo( aItemInfo ) )
aItemInfos.push_back( aItemInfo );
aDdeLinkInfo.Items = ContainerHelper::vectorToSequence( aItemInfos );
aLinkInfo.Data <<= aDdeLinkInfo;
}
break;
default:
aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::UNKNOWN;
}
return aLinkInfo;
}
FunctionLibraryType ExternalLink::getFuncLibraryType() const
{
return (meLinkType == LINKTYPE_LIBRARY) ? meFuncLibType : FUNCLIB_UNKNOWN;
}
sal_Int16 ExternalLink::getCalcSheetIndex( sal_Int32 nTabId ) const
{
OSL_ENSURE( meLinkType == LINKTYPE_INTERNAL, "ExternalLink::getCalcSheetIndex - invalid link type" );
OSL_ENSURE( (nTabId == 0) || (getFilterType() == FILTER_OOXML) || (getBiff() == BIFF8),
"ExternalLink::getCalcSheetIndex - invalid sheet index" );
return ContainerHelper::getVectorElement( maCalcSheets, nTabId, -1 );
}
sal_Int32 ExternalLink::getDocumentLinkIndex() const
{
OSL_ENSURE( meLinkType == LINKTYPE_EXTERNAL, "ExternalLink::getDocumentLinkIndex - invalid link type" );
return mxDocLink.is() ? mxDocLink->getTokenIndex() : -1;
}
sal_Int32 ExternalLink::getSheetCacheIndex( sal_Int32 nTabId ) const
{
OSL_ENSURE( meLinkType == LINKTYPE_EXTERNAL, "ExternalLink::getSheetCacheIndex - invalid link type" );
OSL_ENSURE( (nTabId == 0) || (getFilterType() == FILTER_OOXML) || (getBiff() == BIFF8),
"ExternalLink::getSheetCacheIndex - invalid sheet index" );
return ContainerHelper::getVectorElement( maSheetCaches, nTabId, -1 );
}
Reference< XExternalSheetCache > ExternalLink::getSheetCache( sal_Int32 nTabId ) const
{
sal_Int32 nCacheIdx = getSheetCacheIndex( nTabId );
if( mxDocLink.is() && (nCacheIdx >= 0) ) try
{
// existing mxDocLink implies that this is an external link
Reference< XExternalSheetCache > xSheetCache( mxDocLink->getByIndex( nCacheIdx ), UNO_QUERY_THROW );
return xSheetCache;
}
catch( Exception& )
{
}
return 0;
}
void ExternalLink::getSheetRange( LinkSheetRange& orSheetRange, sal_Int32 nTabId1, sal_Int32 nTabId2 ) const
{
switch( meLinkType )
{
case LINKTYPE_SAME:
orSheetRange.setSameSheet();
break;
case LINKTYPE_SELF:
case LINKTYPE_INTERNAL:
orSheetRange.setRange( nTabId1, nTabId2 );
break;
case LINKTYPE_EXTERNAL:
{
sal_Int32 nDocLinkIdx = getDocumentLinkIndex();
switch( getFilterType() )
{
case FILTER_OOXML:
// BIFF12: passed indexes point into sheet list of EXTSHEETLIST
orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
break;
case FILTER_BIFF:
switch( getBiff() )
{
case BIFF2:
case BIFF3:
case BIFF4:
orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
break;
case BIFF5:
// BIFF5: first sheet from this external link, last sheet is passed in nTabId2
if( const ExternalLink* pExtLink2 = getExternalLinks().getExternalLink( nTabId2 ).get() )
if( (pExtLink2->getLinkType() == LINKTYPE_EXTERNAL) && (maTargetUrl == pExtLink2->getTargetUrl()) )
orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex(), pExtLink2->getSheetCacheIndex() );
break;
case BIFF8:
// BIFF8: passed indexes point into sheet list of EXTERNALBOOK
orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
break;
case BIFF_UNKNOWN: break;
}
break;
case FILTER_UNKNOWN: break;
}
}
break;
default:
// unsupported/unexpected link type: #REF! error
orSheetRange.setDeleted();
}
}
ExternalNameRef ExternalLink::getNameByIndex( sal_Int32 nIndex ) const
{
return maExtNames.get( nIndex );
}
// private --------------------------------------------------------------------
#define OOX_TARGETTYPE_EXTLINK CREATE_OFFICEDOC_RELATION_TYPE( "externalLinkPath" )
#define OOX_TARGETTYPE_LIBRARY CREATE_MSOFFICE_RELATION_TYPE( "xlExternalLinkPath/xlLibrary" )
void ExternalLink::setExternalTargetUrl( const OUString& rTargetUrl, const OUString& rTargetType )
{
meLinkType = LINKTYPE_UNKNOWN;
if( rTargetType == OOX_TARGETTYPE_EXTLINK )
{
maTargetUrl = getBaseFilter().getAbsoluteUrl( rTargetUrl );
if( maTargetUrl.getLength() > 0 )
meLinkType = LINKTYPE_EXTERNAL;
}
else if( rTargetType == OOX_TARGETTYPE_LIBRARY )
{
meLinkType = LINKTYPE_LIBRARY;
meFuncLibType = getFormulaParser().getFuncLibTypeFromLibraryName( rTargetUrl );
}
OSL_ENSURE( meLinkType != LINKTYPE_UNKNOWN, "ExternalLink::setExternalTargetUrl - empty target URL or unknown target type" );
// create the external document link API object that will contain the sheet caches
if( meLinkType == LINKTYPE_EXTERNAL ) try
{
PropertySet aDocProps( getDocument() );
Reference< XExternalDocLinks > xDocLinks( aDocProps.getAnyProperty( PROP_ExternalDocLinks ), UNO_QUERY_THROW );
mxDocLink = xDocLinks->addDocLink( maTargetUrl );
}
catch( Exception& )
{
}
}
void ExternalLink::setDdeOleTargetUrl( const OUString& rClassName, const OUString& rTargetUrl, ExternalLinkType eLinkType )
{
maClassName = rClassName;
maTargetUrl = rTargetUrl;
meLinkType = ((maClassName.getLength() > 0) && (maTargetUrl.getLength() > 0)) ? eLinkType : LINKTYPE_UNKNOWN;
OSL_ENSURE( meLinkType == eLinkType, "ExternalLink::setDdeOleTargetUrl - missing classname or target" );
}
void ExternalLink::parseExternalReference( const Relations& rRelations, const OUString& rRelId )
{
if( const Relation* pRelation = rRelations.getRelationFromRelId( rRelId ) )
setExternalTargetUrl( pRelation->maTarget, pRelation->maType );
}
OUString ExternalLink::parseBiffTargetUrl( const OUString& rBiffTargetUrl )
{
meLinkType = LINKTYPE_UNKNOWN;
OUString aClassName, aTargetUrl, aSheetName;
switch( getAddressConverter().parseBiffTargetUrl( aClassName, aTargetUrl, aSheetName, rBiffTargetUrl ) )
{
case BIFF_TARGETTYPE_URL:
if( aTargetUrl.getLength() == 0 )
{
meLinkType = (aSheetName.getLength() > 0) ? LINKTYPE_INTERNAL : LINKTYPE_SELF;
}
else if( (aTargetUrl.getLength() == 1) && (aTargetUrl[ 0 ] == ':') )
{
if( getBiff() >= BIFF4 )
meLinkType = LINKTYPE_ANALYSIS;
}
else if( (aTargetUrl.getLength() > 1) || (aTargetUrl[ 0 ] != ' ') )
{
setExternalTargetUrl( aTargetUrl, OOX_TARGETTYPE_EXTLINK );
}
break;
case BIFF_TARGETTYPE_SAMESHEET:
OSL_ENSURE( (aTargetUrl.getLength() == 0) && (aSheetName.getLength() == 0), "ExternalLink::parseBiffTargetUrl - unexpected target or sheet name" );
meLinkType = LINKTYPE_SAME;
break;
case BIFF_TARGETTYPE_LIBRARY:
OSL_ENSURE( aSheetName.getLength() == 0, "ExternalLink::parseBiffTargetUrl - unexpected sheet name" );
setExternalTargetUrl( aTargetUrl, OOX_TARGETTYPE_LIBRARY );
break;
case BIFF_TARGETTYPE_DDE_OLE:
setDdeOleTargetUrl( aClassName, aTargetUrl, LINKTYPE_MAYBE_DDE_OLE );
break;
case BIFF_TARGETTYPE_UNKNOWN:
break;
}
return aSheetName;
}
void ExternalLink::insertExternalSheet( const OUString& rSheetName )
{
OSL_ENSURE( rSheetName.getLength() > 0, "ExternalLink::insertExternalSheet - empty sheet name" );
if( mxDocLink.is() )
{
Reference< XExternalSheetCache > xSheetCache = mxDocLink->addSheetCache( rSheetName, false );
sal_Int32 nCacheIdx = xSheetCache.is() ? xSheetCache->getTokenIndex() : -1;
maSheetCaches.push_back( nCacheIdx );
}
}
ExternalNameRef ExternalLink::createExternalName()
{
ExternalNameRef xExtName( new ExternalName( *this ) );
maExtNames.push_back( xExtName );
return xExtName;
}
// ============================================================================
RefSheetsModel::RefSheetsModel() :
mnExtRefId( -1 ),
mnTabId1( -1 ),
mnTabId2( -1 )
{
}
void RefSheetsModel::readBiff12Data( SequenceInputStream& rStrm )
{
rStrm >> mnExtRefId >> mnTabId1 >> mnTabId2;
}
void RefSheetsModel::readBiff8Data( BiffInputStream& rStrm )
{
mnExtRefId = rStrm.readuInt16();
mnTabId1 = rStrm.readInt16();
mnTabId2 = rStrm.readInt16();
}
// ----------------------------------------------------------------------------
ExternalLinkBuffer::ExternalLinkBuffer( const WorkbookHelper& rHelper ) :
WorkbookHelper( rHelper ),
mxSelfRef( new ExternalLink( rHelper ) ),
mbUseRefSheets( false )
{
mxSelfRef->setSelfLinkType();
}
ExternalLinkRef ExternalLinkBuffer::importExternalReference( const AttributeList& rAttribs )
{
ExternalLinkRef xExtLink = createExternalLink();
xExtLink->importExternalReference( rAttribs );
maExtLinks.push_back( xExtLink );
return xExtLink;
}
ExternalLinkRef ExternalLinkBuffer::importExternalRef( SequenceInputStream& rStrm )
{
mbUseRefSheets = true;
ExternalLinkRef xExtLink = createExternalLink();
xExtLink->importExternalRef( rStrm );
maExtLinks.push_back( xExtLink );
return xExtLink;
}
void ExternalLinkBuffer::importExternalSelf( SequenceInputStream& rStrm )
{
mbUseRefSheets = true;
createExternalLink()->importExternalSelf( rStrm );
}
void ExternalLinkBuffer::importExternalSame( SequenceInputStream& rStrm )
{
mbUseRefSheets = true;
createExternalLink()->importExternalSame( rStrm );
}
void ExternalLinkBuffer::importExternalAddin( SequenceInputStream& rStrm )
{
mbUseRefSheets = true;
createExternalLink()->importExternalAddin( rStrm );
}
void ExternalLinkBuffer::importExternalSheets( SequenceInputStream& rStrm )
{
OSL_ENSURE( mbUseRefSheets, "ExternalLinkBuffer::importExternalSheets - missing EXTERNALREFS records" );
mbUseRefSheets = true;
OSL_ENSURE( maRefSheets.empty(), "ExternalLinkBuffer::importExternalSheets - multiple EXTERNALSHEETS records" );
maRefSheets.clear();
sal_Int32 nRefCount;
rStrm >> nRefCount;
size_t nMaxCount = getLimitedValue< size_t, sal_Int64 >( nRefCount, 0, rStrm.getRemaining() / 12 );
maRefSheets.reserve( nMaxCount );
for( size_t nRefId = 0; !rStrm.isEof() && (nRefId < nMaxCount); ++nRefId )
{
RefSheetsModel aRefSheets;
aRefSheets.readBiff12Data( rStrm );
maRefSheets.push_back( aRefSheets );
}
}
ExternalLinkRef ExternalLinkBuffer::importExternSheet( BiffInputStream& rStrm )
{
OSL_ENSURE( getBiff() <= BIFF5, "ExternalLinkBuffer::importExternSheet - wrong BIFF version" );
ExternalLinkRef xExtLink = createExternalLink();
xExtLink->importExternSheet( rStrm );
return xExtLink;
}
ExternalLinkRef ExternalLinkBuffer::importExternalBook( BiffInputStream& rStrm )
{
ExternalLinkRef xExtLink = createExternalLink();
xExtLink->importExternalBook( rStrm );
return xExtLink;
}
void ExternalLinkBuffer::importExternalName( BiffInputStream& rStrm )
{
if( !maLinks.empty() )
maLinks.back()->importExternalName( rStrm );
}
void ExternalLinkBuffer::importExternSheet8( BiffInputStream& rStrm )
{
OSL_ENSURE( getBiff() == BIFF8, "ExternalLinkBuffer::importExternSheet8 - wrong BIFF version" );
sal_uInt16 nRefCount;
rStrm >> nRefCount;
OSL_ENSURE( static_cast< sal_Int64 >( nRefCount * 6 ) == rStrm.getRemaining(), "ExternalLinkBuffer::importExternSheet8 - invalid count" );
nRefCount = static_cast< sal_uInt16 >( ::std::min< sal_Int64 >( nRefCount, rStrm.getRemaining() / 6 ) );
/* #i104057# A weird external XLS generator writes multiple EXTERNSHEET
records instead of only one as expected. Surprisingly, Excel seems to
insert the entries of the second record before the entries of the first
record. */
maRefSheets.insert( maRefSheets.begin(), nRefCount, RefSheetsModel() );
for( RefSheetsModelVec::iterator aIt = maRefSheets.begin(), aEnd = aIt + nRefCount; !rStrm.isEof() && (aIt != aEnd); ++aIt )
aIt->readBiff8Data( rStrm );
}
Sequence< ExternalLinkInfo > ExternalLinkBuffer::getLinkInfos() const
{
::std::vector< ExternalLinkInfo > aLinkInfos;
// XML formula parser also used in BIFF12 documents, e.g. replacement formulas in unsupported conditional formattings
OSL_ENSURE( getFilterType() == FILTER_OOXML, "ExternalLinkBuffer::getLinkInfos - unexpected file format" );
// add entry for implicit index 0 (self reference to this document)
aLinkInfos.push_back( mxSelfRef->getLinkInfo() );
for( ExternalLinkVec::const_iterator aIt = maExtLinks.begin(), aEnd = maExtLinks.end(); aIt != aEnd; ++aIt )
aLinkInfos.push_back( (*aIt)->getLinkInfo() );
return ContainerHelper::vectorToSequence( aLinkInfos );
}
ExternalLinkRef ExternalLinkBuffer::getExternalLink( sal_Int32 nRefId, bool bUseRefSheets ) const
{
ExternalLinkRef xExtLink;
switch( getFilterType() )
{
case FILTER_OOXML:
// OOXML: 0 = this document, otherwise one-based index into link list
if( !bUseRefSheets || !mbUseRefSheets )
xExtLink = (nRefId == 0) ? mxSelfRef : maLinks.get( nRefId - 1 );
// BIFF12: zero-based index into ref-sheets list
else if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
xExtLink = maLinks.get( pRefSheets->mnExtRefId );
break;
case FILTER_BIFF:
switch( getBiff() )
{
case BIFF2:
case BIFF3:
case BIFF4:
// one-based index to EXTERNSHEET records
xExtLink = maLinks.get( nRefId - 1 );
break;
case BIFF5:
if( nRefId < 0 )
{
// internal links in formula tokens have negative index
xExtLink = maLinks.get( -nRefId - 1 );
if( xExtLink.get() && !xExtLink->isInternalLink() )
xExtLink.reset();
}
else
{
// one-based index to EXTERNSHEET records
xExtLink = maLinks.get( nRefId - 1 );
}
break;
case BIFF8:
// zero-based index into REF list in EXTERNSHEET record
if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
xExtLink = maLinks.get( pRefSheets->mnExtRefId );
break;
case BIFF_UNKNOWN: break;
}
break;
case FILTER_UNKNOWN: break;
}
return xExtLink;
}
LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId, sal_Int16 nTabId1, sal_Int16 nTabId2 ) const
{
OSL_ENSURE( getBiff() <= BIFF5, "ExternalLinkBuffer::getSheetRange - wrong BIFF version" );
LinkSheetRange aSheetRange;
if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() )
pExtLink->getSheetRange( aSheetRange, nTabId1, nTabId2 );
return aSheetRange;
}
LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId ) const
{
OSL_ENSURE( ((getFilterType() == FILTER_OOXML) && mbUseRefSheets) || (getBiff() == BIFF8), "ExternalLinkBuffer::getSheetRange - wrong BIFF version" );
LinkSheetRange aSheetRange;
if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() )
if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
pExtLink->getSheetRange( aSheetRange, pRefSheets->mnTabId1, pRefSheets->mnTabId2 );
return aSheetRange;
}
// private --------------------------------------------------------------------
ExternalLinkRef ExternalLinkBuffer::createExternalLink()
{
ExternalLinkRef xExtLink( new ExternalLink( *this ) );
maLinks.push_back( xExtLink );
return xExtLink;
}
const RefSheetsModel* ExternalLinkBuffer::getRefSheets( sal_Int32 nRefId ) const
{
return ((0 <= nRefId) && (static_cast< size_t >( nRefId ) < maRefSheets.size())) ?
&maRefSheets[ static_cast< size_t >( nRefId ) ] : 0;
}
// ============================================================================
} // namespace xls
} // namespace oox