1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "oox/xls/externallinkbuffer.hxx"
29 
30 #include <com/sun/star/sheet/ComplexReference.hpp>
31 #include <com/sun/star/sheet/DDELinkInfo.hpp>
32 #include <com/sun/star/sheet/ExternalLinkType.hpp>
33 #include <com/sun/star/sheet/ExternalReference.hpp>
34 #include <com/sun/star/sheet/ReferenceFlags.hpp>
35 #include <com/sun/star/sheet/SingleReference.hpp>
36 #include <com/sun/star/sheet/XDDELinks.hpp>
37 #include <com/sun/star/sheet/XDDELink.hpp>
38 #include <com/sun/star/sheet/XDDELinkResults.hpp>
39 #include <com/sun/star/sheet/XExternalDocLink.hpp>
40 #include <com/sun/star/sheet/XExternalDocLinks.hpp>
41 #include <rtl/strbuf.hxx>
42 #include "oox/core/filterbase.hxx"
43 #include "oox/helper/attributelist.hxx"
44 #include "oox/xls/addressconverter.hxx"
45 #include "oox/xls/biffinputstream.hxx"
46 #include "oox/xls/excelhandlers.hxx"
47 #include "oox/xls/formulaparser.hxx"
48 #include "oox/xls/worksheetbuffer.hxx"
49 
50 namespace oox {
51 namespace xls {
52 
53 // ============================================================================
54 
55 using namespace ::com::sun::star::sheet;
56 using namespace ::com::sun::star::table;
57 using namespace ::com::sun::star::uno;
58 
59 using ::oox::core::Relation;
60 using ::oox::core::Relations;
61 using ::rtl::OString;
62 using ::rtl::OStringBuffer;
63 using ::rtl::OStringToOUString;
64 using ::rtl::OUString;
65 
66 // ============================================================================
67 
68 namespace {
69 
70 const sal_uInt16 BIFF12_EXTERNALBOOK_BOOK   = 0;
71 const sal_uInt16 BIFF12_EXTERNALBOOK_DDE    = 1;
72 const sal_uInt16 BIFF12_EXTERNALBOOK_OLE    = 2;
73 
74 const sal_uInt16 BIFF12_EXTNAME_AUTOMATIC   = 0x0002;
75 const sal_uInt16 BIFF12_EXTNAME_PREFERPIC   = 0x0004;
76 const sal_uInt16 BIFF12_EXTNAME_STDDOCNAME  = 0x0008;
77 const sal_uInt16 BIFF12_EXTNAME_OLEOBJECT   = 0x0010;
78 const sal_uInt16 BIFF12_EXTNAME_ICONIFIED   = 0x0020;
79 
80 const sal_uInt16 BIFF_EXTNAME_BUILTIN       = 0x0001;
81 const sal_uInt16 BIFF_EXTNAME_AUTOMATIC     = 0x0002;
82 const sal_uInt16 BIFF_EXTNAME_PREFERPIC     = 0x0004;
83 const sal_uInt16 BIFF_EXTNAME_STDDOCNAME    = 0x0008;
84 const sal_uInt16 BIFF_EXTNAME_OLEOBJECT     = 0x0010;
85 const sal_uInt16 BIFF_EXTNAME_ICONIFIED     = 0x8000;
86 
87 } // namespace
88 
89 // ============================================================================
90 
91 ExternalNameModel::ExternalNameModel() :
92     mbBuiltIn( false ),
93     mbNotify( false ),
94     mbPreferPic( false ),
95     mbStdDocName( false ),
96     mbOleObj( false ),
97     mbIconified( false )
98 {
99 }
100 
101 // ============================================================================
102 
103 ExternalName::ExternalName( const ExternalLink& rParentLink ) :
104     DefinedNameBase( rParentLink ),
105     mrParentLink( rParentLink ),
106     mnStorageId( 0 ),
107     mbDdeLinkCreated( false )
108 {
109 }
110 
111 void ExternalName::importDefinedName( const AttributeList& rAttribs )
112 {
113     maModel.maName = rAttribs.getXString( XML_name, OUString() );
114     OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importDefinedName - empty name" );
115     // zero-based index into sheet list of externalBook
116     maModel.mnSheet = rAttribs.getInteger( XML_sheetId, -1 );
117 }
118 
119 void ExternalName::importDdeItem( const AttributeList& rAttribs )
120 {
121     maModel.maName = rAttribs.getXString( XML_name, OUString() );
122     OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importDdeItem - empty name" );
123     maExtNameModel.mbOleObj     = false;
124     maExtNameModel.mbStdDocName = rAttribs.getBool( XML_ole, false );
125     maExtNameModel.mbNotify     = rAttribs.getBool( XML_advise, false );
126     maExtNameModel.mbPreferPic  = rAttribs.getBool( XML_preferPic, false );
127 }
128 
129 void ExternalName::importValues( const AttributeList& rAttribs )
130 {
131     setResultSize( rAttribs.getInteger( XML_cols, 1 ), rAttribs.getInteger( XML_rows, 1 ) );
132 }
133 
134 void ExternalName::importOleItem( const AttributeList& rAttribs )
135 {
136     maModel.maName = rAttribs.getXString( XML_name, OUString() );
137     OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importOleItem - empty name" );
138     maExtNameModel.mbOleObj    = true;
139     maExtNameModel.mbNotify    = rAttribs.getBool( XML_advise, false );
140     maExtNameModel.mbPreferPic = rAttribs.getBool( XML_preferPic, false );
141     maExtNameModel.mbIconified = rAttribs.getBool( XML_icon, false );
142 }
143 
144 void ExternalName::importExternalName( SequenceInputStream& rStrm )
145 {
146     rStrm >> maModel.maName;
147     OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importExternalName - empty name" );
148 }
149 
150 void ExternalName::importExternalNameFlags( SequenceInputStream& rStrm )
151 {
152     sal_uInt16 nFlags;
153     sal_Int32 nSheetId;
154     rStrm >> nFlags >> nSheetId;
155     // index into sheet list of EXTSHEETNAMES (one-based in BIFF12)
156     maModel.mnSheet = nSheetId - 1;
157     // no flag for built-in names, as in OOXML...
158     maExtNameModel.mbNotify     = getFlag( nFlags, BIFF12_EXTNAME_AUTOMATIC );
159     maExtNameModel.mbPreferPic  = getFlag( nFlags, BIFF12_EXTNAME_PREFERPIC );
160     maExtNameModel.mbStdDocName = getFlag( nFlags, BIFF12_EXTNAME_STDDOCNAME );
161     maExtNameModel.mbOleObj     = getFlag( nFlags, BIFF12_EXTNAME_OLEOBJECT );
162     maExtNameModel.mbIconified  = getFlag( nFlags, BIFF12_EXTNAME_ICONIFIED );
163     OSL_ENSURE( (mrParentLink.getLinkType() == LINKTYPE_OLE) == maExtNameModel.mbOleObj,
164         "ExternalName::importExternalNameFlags - wrong OLE flag in external name" );
165 }
166 
167 void ExternalName::importDdeItemValues( SequenceInputStream& rStrm )
168 {
169     sal_Int32 nRows, nCols;
170     rStrm >> nRows >> nCols;
171     setResultSize( nCols, nRows );
172 }
173 
174 void ExternalName::importDdeItemBool( SequenceInputStream& rStrm )
175 {
176     appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 );
177 }
178 
179 void ExternalName::importDdeItemDouble( SequenceInputStream& rStrm )
180 {
181     appendResultValue( rStrm.readDouble() );
182 }
183 
184 void ExternalName::importDdeItemError( SequenceInputStream& rStrm )
185 {
186     appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) );
187 }
188 
189 void ExternalName::importDdeItemString( SequenceInputStream& rStrm )
190 {
191     appendResultValue( BiffHelper::readString( rStrm ) );
192 }
193 
194 void ExternalName::importExternalName( BiffInputStream& rStrm )
195 {
196     sal_uInt16 nFlags = 0;
197     if( getBiff() >= BIFF3 )
198     {
199         rStrm >> nFlags;
200         maExtNameModel.mbBuiltIn    = getFlag( nFlags, BIFF_EXTNAME_BUILTIN );
201         maExtNameModel.mbNotify     = getFlag( nFlags, BIFF_EXTNAME_AUTOMATIC );
202         maExtNameModel.mbPreferPic  = getFlag( nFlags, BIFF_EXTNAME_PREFERPIC );
203 
204         // BIFF5-BIFF8: sheet index for sheet-local names, OLE settings
205         if( getBiff() >= BIFF5 )
206         {
207             maExtNameModel.mbStdDocName = getFlag( nFlags, BIFF_EXTNAME_STDDOCNAME );
208             maExtNameModel.mbOleObj     = getFlag( nFlags, BIFF_EXTNAME_OLEOBJECT );
209             maExtNameModel.mbIconified  = getFlag( nFlags, BIFF_EXTNAME_ICONIFIED );
210 
211             if( maExtNameModel.mbOleObj )
212             {
213                 rStrm >> mnStorageId;
214             }
215             else
216             {
217                 /*  Import the reference ID for names that are sheet-local in
218                     the external document. This index will be resolved later to
219                     the index of the external sheet cache which is able to
220                     provide the name of the sheet related to this defined name.
221                     - BIFF5: one-based index to EXTERNSHEET record containing
222                         the document and sheet name
223                     - BIFF8: one-based index into EXTERNALBOOK sheet name list
224                     The value zero means this external name is a global name.
225                  */
226                 rStrm.skip( 2 );
227                 maModel.mnSheet = rStrm.readuInt16();
228             }
229         }
230     }
231 
232     maModel.maName = (getBiff() == BIFF8) ?
233         rStrm.readUniStringBody( rStrm.readuInt8() ) :
234         rStrm.readByteStringUC( false, getTextEncoding() );
235     OSL_ENSURE( maModel.maName.getLength() > 0, "ExternalName::importExternalName - empty name" );
236 
237     // load cell references that are stored in hidden external names (seen in BIFF3-BIFF4)
238     bool bHiddenRef = (getBiff() <= BIFF4) && (maModel.maName.getLength() > 1) && (maModel.maName[ 0 ] == '\x01') && (rStrm.getRemaining() > 2);
239     switch( mrParentLink.getLinkType() )
240     {
241         case LINKTYPE_INTERNAL:
242             // cell references to other internal sheets are stored in hidden external names
243             if( bHiddenRef && (getBiff() == BIFF4) && isWorkbookFile() )
244             {
245                 ApiTokenSequence aTokens = importBiffFormula( mrParentLink.getCalcSheetIndex(), rStrm );
246                 extractReference( aTokens );
247             }
248         break;
249 
250         case LINKTYPE_EXTERNAL:
251             // cell references to other documents are stored in hidden external names
252             if( bHiddenRef )
253             {
254                 ApiTokenSequence aTokens = importBiffFormula( 0, rStrm );
255                 extractExternalReference( aTokens );
256             }
257         break;
258 
259         case LINKTYPE_DDE:
260         case LINKTYPE_OLE:
261         case LINKTYPE_MAYBE_DDE_OLE:
262             // DDE/OLE link results
263             if( rStrm.getRemaining() > 3 )
264             {
265                 bool bBiff8 = getBiff() == BIFF8;
266                 sal_Int32 nCols = rStrm.readuInt8();
267                 sal_Int32 nRows = rStrm.readuInt16();
268                 if( bBiff8 ) { ++nCols; ++nRows; } else if( nCols == 0 ) nCols = 256;
269                 setResultSize( nCols, nRows );
270 
271                 bool bLoop = true;
272                 while( bLoop && !rStrm.isEof() && (maCurrIt != maResults.end()) )
273                 {
274                     switch( rStrm.readuInt8() )
275                     {
276                         case BIFF_DATATYPE_EMPTY:
277                             appendResultValue( OUString() );
278                             rStrm.skip( 8 );
279                         break;
280                         case BIFF_DATATYPE_DOUBLE:
281                             appendResultValue( rStrm.readDouble() );
282                         break;
283                         case BIFF_DATATYPE_STRING:
284                             appendResultValue( bBiff8 ? rStrm.readUniString() : rStrm.readByteStringUC( false, getTextEncoding() ) );
285                         break;
286                         case BIFF_DATATYPE_BOOL:
287                             appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 );
288                             rStrm.skip( 7 );
289                         break;
290                         case BIFF_DATATYPE_ERROR:
291                             appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) );
292                             rStrm.skip( 7 );
293                         break;
294                         default:
295                             bLoop = false;
296                     }
297                 }
298                 OSL_ENSURE( bLoop && !rStrm.isEof() && (maCurrIt == maResults.end()),
299                     "ExternalName::importExternalName - stream error in result set" );
300             }
301         break;
302 
303         default:;
304     }
305 }
306 
307 #if 0
308 sal_Int32 ExternalName::getSheetCacheIndex() const
309 {
310     OSL_ENSURE( mrParentLink.getLinkType() == LINKTYPE_DDE, "ExternalName::getSheetCacheIndex - unexpected link type" );
311     sal_Int32 nCacheIdx = -1;
312     switch( getFilterType() )
313     {
314         case FILTER_OOXML:
315             // OOXML/BIFF12: zero-based index into sheet list, -1 means global name
316             if( maModel.mnSheet >= 0 )
317                 nCacheIdx = mrParentLink.getSheetIndex( maModel.mnSheet );
318         break;
319         case FILTER_BIFF:
320             switch( getBiff() )
321             {
322                 case BIFF2:
323                 case BIFF3:
324                 case BIFF4:
325                 break;
326                 case BIFF5:
327                     if( maModel.mnSheet > 0 )
328                         if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( maModel.mnSheet ).get() )
329                             if( pExtLink->getLinkType() == LINKTYPE_EXTERNAL )
330                                 nCacheIdx = pExtLink->getSheetIndex();
331                 break;
332                 case BIFF8:
333                     if( maModel.mnSheet > 0 )
334                         nCacheIdx = mrParentLink.getSheetIndex( maModel.mnSheet - 1 );
335                 break;
336                 case BIFF_UNKNOWN:
337                 break;
338             }
339         break;
340         case FILTER_UNKNOWN:
341         break;
342     }
343     return nCacheIdx;
344 }
345 #endif
346 
347 bool ExternalName::getDdeItemInfo( DDEItemInfo& orItemInfo ) const
348 {
349     if( (mrParentLink.getLinkType() == LINKTYPE_DDE) && (maModel.maName.getLength() > 0) )
350     {
351         orItemInfo.Item = maModel.maName;
352         orItemInfo.Results = ContainerHelper::matrixToSequenceSequence( maResults );
353         return true;
354     }
355     return false;
356 }
357 
358 bool ExternalName::getDdeLinkData( OUString& orDdeServer, OUString& orDdeTopic, OUString& orDdeItem )
359 {
360     if( (mrParentLink.getLinkType() == LINKTYPE_DDE) && (maModel.maName.getLength() > 0) )
361     {
362         // try to create a DDE link and to set the imported link results
363         if( !mbDdeLinkCreated ) try
364         {
365             PropertySet aDocProps( getDocument() );
366             Reference< XDDELinks > xDdeLinks( aDocProps.getAnyProperty( PROP_DDELinks ), UNO_QUERY_THROW );
367             mxDdeLink = xDdeLinks->addDDELink( mrParentLink.getClassName(), mrParentLink.getTargetUrl(), maModel.maName, ::com::sun::star::sheet::DDELinkMode_DEFAULT );
368             mbDdeLinkCreated = true;    // ignore if setting results fails
369             if( !maResults.empty() )
370             {
371                 Reference< XDDELinkResults > xResults( mxDdeLink, UNO_QUERY_THROW );
372                 xResults->setResults( ContainerHelper::matrixToSequenceSequence( maResults ) );
373             }
374         }
375         catch( Exception& )
376         {
377             OSL_ENSURE( false, "ExternalName::getDdeLinkData - cannot create DDE link" );
378         }
379         // get link data from created DDE link
380         if( mxDdeLink.is() )
381         {
382             orDdeServer = mxDdeLink->getApplication();
383             orDdeTopic = mxDdeLink->getTopic();
384             orDdeItem = mxDdeLink->getItem();
385             return true;
386         }
387     }
388     return false;
389 }
390 
391 // private --------------------------------------------------------------------
392 
393 namespace {
394 
395 void lclSetSheetCacheIndex( SingleReference& orApiRef, sal_Int32 nCacheIdx )
396 {
397     using namespace ::com::sun::star::sheet::ReferenceFlags;
398     setFlag( orApiRef.Flags, SHEET_RELATIVE, false );
399     setFlag( orApiRef.Flags, SHEET_3D, true );
400     orApiRef.Sheet = nCacheIdx;
401 }
402 
403 } // namespace
404 
405 void ExternalName::extractExternalReference( const ApiTokenSequence& rTokens )
406 {
407     OSL_ENSURE( (getFilterType() == FILTER_BIFF) && (getBiff() <= BIFF4), "ExternalName::setExternalReference - unexpected call" );
408     sal_Int32 nDocLinkIdx = mrParentLink.getDocumentLinkIndex();
409     sal_Int32 nCacheIdx = mrParentLink.getSheetCacheIndex();
410     if( (nDocLinkIdx >= 0) && (nCacheIdx >= 0) )
411     {
412         ExternalReference aExtApiRef;
413         aExtApiRef.Index = nDocLinkIdx;
414 
415         Any aRefAny = getFormulaParser().extractReference( rTokens );
416         if( aRefAny.has< SingleReference >() )
417         {
418             SingleReference aApiRef;
419             aRefAny >>= aApiRef;
420             lclSetSheetCacheIndex( aApiRef, nCacheIdx );
421             aExtApiRef.Reference <<= aApiRef;
422             maRefAny <<= aExtApiRef;
423         }
424         else if( aRefAny.has< ComplexReference >() )
425         {
426             ComplexReference aApiRef;
427             aRefAny >>= aApiRef;
428             lclSetSheetCacheIndex( aApiRef.Reference1, nCacheIdx );
429             lclSetSheetCacheIndex( aApiRef.Reference2, nCacheIdx );
430             aExtApiRef.Reference <<= aApiRef;
431             maRefAny <<= aExtApiRef;
432         }
433     }
434 }
435 
436 void ExternalName::setResultSize( sal_Int32 nColumns, sal_Int32 nRows )
437 {
438     OSL_ENSURE( (mrParentLink.getLinkType() == LINKTYPE_DDE) || (mrParentLink.getLinkType() == LINKTYPE_OLE) ||
439         (mrParentLink.getLinkType() == LINKTYPE_MAYBE_DDE_OLE), "ExternalName::setResultSize - wrong link type" );
440     OSL_ENSURE( (nRows > 0) && (nColumns > 0), "ExternalName::setResultSize - invalid matrix size" );
441     const CellAddress& rMaxPos = getAddressConverter().getMaxApiAddress();
442     if( (0 < nRows) && (nRows <= rMaxPos.Row + 1) && (0 < nColumns) && (nColumns <= rMaxPos.Column + 1) )
443         maResults.resize( static_cast< size_t >( nColumns ), static_cast< size_t >( nRows ), Any( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) ) );
444     else
445         maResults.clear();
446     maCurrIt = maResults.begin();
447 }
448 
449 // ============================================================================
450 
451 void LinkSheetRange::setDeleted()
452 {
453     meType = LINKSHEETRANGE_INTERNAL;
454     mnDocLink = mnFirst = mnLast = -1;
455 }
456 
457 void LinkSheetRange::setSameSheet()
458 {
459     meType = LINKSHEETRANGE_SAMESHEET;
460     mnDocLink = -1;
461     mnFirst = mnLast = 0;
462 }
463 
464 void LinkSheetRange::setRange( sal_Int32 nFirst, sal_Int32 nLast )
465 {
466     meType = LINKSHEETRANGE_INTERNAL;
467     mnDocLink = -1;
468     mnFirst = ::std::min( nFirst, nLast );
469     mnLast = ::std::max( nFirst, nLast );
470 }
471 
472 void LinkSheetRange::setExternalRange( sal_Int32 nDocLink, sal_Int32 nFirst, sal_Int32 nLast )
473 {
474     if( nDocLink < 0 )
475     {
476         setDeleted();
477     }
478     else
479     {
480         meType = LINKSHEETRANGE_EXTERNAL;
481         mnDocLink = nDocLink;
482         mnFirst = ::std::min( nFirst, nLast );
483         mnLast = ::std::max( nFirst, nLast );
484     }
485 }
486 
487 // ============================================================================
488 
489 ExternalLink::ExternalLink( const WorkbookHelper& rHelper ) :
490     WorkbookHelper( rHelper ),
491     meLinkType( LINKTYPE_UNKNOWN ),
492     meFuncLibType( FUNCLIB_UNKNOWN )
493 {
494 }
495 
496 void ExternalLink::importExternalReference( const AttributeList& rAttribs )
497 {
498     maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
499 }
500 
501 void ExternalLink::importExternalBook( const Relations& rRelations, const AttributeList& rAttribs )
502 {
503     parseExternalReference( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
504 }
505 
506 void ExternalLink::importSheetName( const AttributeList& rAttribs )
507 {
508     insertExternalSheet( rAttribs.getXString( XML_val, OUString() ) );
509 }
510 
511 void ExternalLink::importDefinedName( const AttributeList& rAttribs )
512 {
513     createExternalName()->importDefinedName( rAttribs );
514 }
515 
516 void ExternalLink::importDdeLink( const AttributeList& rAttribs )
517 {
518     OUString aDdeService = rAttribs.getXString( XML_ddeService, OUString() );
519     OUString aDdeTopic = rAttribs.getXString( XML_ddeTopic, OUString() );
520     setDdeOleTargetUrl( aDdeService, aDdeTopic, LINKTYPE_DDE );
521 }
522 
523 ExternalNameRef ExternalLink::importDdeItem( const AttributeList& rAttribs )
524 {
525     ExternalNameRef xExtName = createExternalName();
526     xExtName->importDdeItem( rAttribs );
527     return xExtName;
528 }
529 
530 void ExternalLink::importOleLink( const Relations& rRelations, const AttributeList& rAttribs )
531 {
532     OUString aProgId = rAttribs.getXString( XML_progId, OUString() );
533     OUString aTargetUrl = rRelations.getExternalTargetFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
534     setDdeOleTargetUrl( aProgId, aTargetUrl, LINKTYPE_OLE );
535 }
536 
537 ExternalNameRef ExternalLink::importOleItem( const AttributeList& rAttribs )
538 {
539     ExternalNameRef xExtName = createExternalName();
540     xExtName->importOleItem( rAttribs );
541     return xExtName;
542 }
543 
544 void ExternalLink::importExternalRef( SequenceInputStream& rStrm )
545 {
546     rStrm >> maRelId;
547 }
548 
549 void ExternalLink::importExternalSelf( SequenceInputStream& )
550 {
551     meLinkType = LINKTYPE_SELF;
552 }
553 
554 void ExternalLink::importExternalSame( SequenceInputStream& )
555 {
556     meLinkType = LINKTYPE_SAME;
557 }
558 
559 void ExternalLink::importExternalAddin( SequenceInputStream& )
560 {
561     meLinkType = LINKTYPE_UNKNOWN;
562 }
563 
564 void ExternalLink::importExternalBook( const Relations& rRelations, SequenceInputStream& rStrm )
565 {
566     switch( rStrm.readuInt16() )
567     {
568         case BIFF12_EXTERNALBOOK_BOOK:
569             parseExternalReference( rRelations, BiffHelper::readString( rStrm ) );
570         break;
571         case BIFF12_EXTERNALBOOK_DDE:
572         {
573             OUString aDdeService, aDdeTopic;
574             rStrm >> aDdeService >> aDdeTopic;
575             setDdeOleTargetUrl( aDdeService, aDdeTopic, LINKTYPE_DDE );
576         }
577         break;
578         case BIFF12_EXTERNALBOOK_OLE:
579         {
580             OUString aTargetUrl = rRelations.getExternalTargetFromRelId( BiffHelper::readString( rStrm ) );
581             OUString aProgId = BiffHelper::readString( rStrm );
582             setDdeOleTargetUrl( aProgId, aTargetUrl, LINKTYPE_OLE );
583         }
584         break;
585         default:
586             OSL_ENSURE( false, "ExternalLink::importExternalBook - unknown link type" );
587     }
588 }
589 
590 void ExternalLink::importExtSheetNames( SequenceInputStream& rStrm )
591 {
592     // load external sheet names and create the sheet caches in the Calc document
593     OSL_ENSURE( (meLinkType == LINKTYPE_EXTERNAL) || (meLinkType == LINKTYPE_LIBRARY),
594         "ExternalLink::importExtSheetNames - invalid link type" );
595     if( meLinkType == LINKTYPE_EXTERNAL )   // ignore sheets of external libraries
596         for( sal_Int32 nSheet = 0, nCount = rStrm.readInt32(); !rStrm.isEof() && (nSheet < nCount); ++nSheet )
597             insertExternalSheet( BiffHelper::readString( rStrm ) );
598 }
599 
600 ExternalNameRef ExternalLink::importExternalName( SequenceInputStream& rStrm )
601 {
602     ExternalNameRef xExtName = createExternalName();
603     xExtName->importExternalName( rStrm );
604     return xExtName;
605 }
606 
607 void ExternalLink::importExternSheet( BiffInputStream& rStrm )
608 {
609     OStringBuffer aTargetBuffer( rStrm.readByteString( false, true ) );
610     // references to own sheets have wrong string length field (off by 1)
611     if( (aTargetBuffer.getLength() > 0) && (aTargetBuffer[ 0 ] == 3) )
612         aTargetBuffer.append( static_cast< sal_Char >( rStrm.readuInt8() ) );
613     // parse the encoded URL
614     OUString aBiffTarget = OStringToOUString( aTargetBuffer.makeStringAndClear(), getTextEncoding() );
615     OUString aSheetName = parseBiffTargetUrl( aBiffTarget );
616     switch( meLinkType )
617     {
618         case LINKTYPE_INTERNAL:
619             maCalcSheets.push_back( getWorksheets().getCalcSheetIndex( aSheetName ) );
620         break;
621         case LINKTYPE_EXTERNAL:
622             insertExternalSheet( (aSheetName.getLength() > 0) ? aSheetName : WorksheetBuffer::getBaseFileName( maTargetUrl ) );
623         break;
624         default:;
625     }
626 }
627 
628 void ExternalLink::importExternalBook( BiffInputStream& rStrm )
629 {
630     OUString aTarget;
631     sal_uInt16 nSheetCount;
632     rStrm >> nSheetCount;
633     if( rStrm.getRemaining() == 2 )
634     {
635         if( rStrm.readuInt8() == 1 )
636         {
637             sal_Char cChar = static_cast< sal_Char >( rStrm.readuInt8() );
638             if( cChar != 0 )
639                 aTarget = OStringToOUString( OString( cChar ), getTextEncoding() );
640         }
641     }
642     else if( rStrm.getRemaining() >= 3 )
643     {
644         // NUL characters may occur
645         aTarget = rStrm.readUniString( true );
646     }
647 
648     // parse the encoded URL
649     OUString aDummySheetName = parseBiffTargetUrl( aTarget );
650     OSL_ENSURE( aDummySheetName.getLength() == 0, "ExternalLink::importExternalBook - sheet name in encoded URL" );
651     (void)aDummySheetName;  // prevent compiler warning
652 
653     // load external sheet names and create the sheet caches in the Calc document
654     if( meLinkType == LINKTYPE_EXTERNAL )
655         for( sal_uInt16 nSheet = 0; !rStrm.isEof() && (nSheet < nSheetCount); ++nSheet )
656             insertExternalSheet( rStrm.readUniString() );
657 }
658 
659 void ExternalLink::importExternalName( BiffInputStream& rStrm )
660 {
661     ExternalNameRef xExtName = createExternalName();
662     xExtName->importExternalName( rStrm );
663     switch( meLinkType )
664     {
665         case LINKTYPE_DDE:
666             OSL_ENSURE( !xExtName->isOleObject(), "ExternalLink::importExternalName - OLE object in DDE link" );
667         break;
668         case LINKTYPE_OLE:
669             OSL_ENSURE( xExtName->isOleObject(), "ExternalLink::importExternalName - anything but OLE object in OLE link" );
670         break;
671         case LINKTYPE_MAYBE_DDE_OLE:
672             meLinkType = xExtName->isOleObject() ? LINKTYPE_OLE : LINKTYPE_DDE;
673         break;
674         default:
675             OSL_ENSURE( !xExtName->isOleObject(), "ExternalLink::importExternalName - OLE object in external name" );
676     }
677 }
678 
679 ExternalLinkInfo ExternalLink::getLinkInfo() const
680 {
681     ExternalLinkInfo aLinkInfo;
682     switch( meLinkType )
683     {
684         case LINKTYPE_SELF:
685         case LINKTYPE_SAME:
686         case LINKTYPE_INTERNAL:
687             aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::SELF;
688         break;
689         case LINKTYPE_EXTERNAL:
690             aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::DOCUMENT;
691             aLinkInfo.Data <<= maTargetUrl;
692         break;
693         case LINKTYPE_LIBRARY:
694             // parser will return library function names in OPCODE_BAD string tokens
695             aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::SPECIAL;
696         break;
697         case LINKTYPE_DDE:
698         {
699             aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::DDE;
700             DDELinkInfo aDdeLinkInfo;
701             aDdeLinkInfo.Service = maClassName;
702             aDdeLinkInfo.Topic = maTargetUrl;
703             ::std::vector< DDEItemInfo > aItemInfos;
704             DDEItemInfo aItemInfo;
705             for( ExternalNameVector::const_iterator aIt = maExtNames.begin(), aEnd = maExtNames.end(); aIt != aEnd; ++aIt )
706                 if( (*aIt)->getDdeItemInfo( aItemInfo ) )
707                     aItemInfos.push_back( aItemInfo );
708             aDdeLinkInfo.Items = ContainerHelper::vectorToSequence( aItemInfos );
709             aLinkInfo.Data <<= aDdeLinkInfo;
710         }
711         break;
712         default:
713             aLinkInfo.Type = ::com::sun::star::sheet::ExternalLinkType::UNKNOWN;
714     }
715     return aLinkInfo;
716 }
717 
718 FunctionLibraryType ExternalLink::getFuncLibraryType() const
719 {
720     return (meLinkType == LINKTYPE_LIBRARY) ? meFuncLibType : FUNCLIB_UNKNOWN;
721 }
722 
723 sal_Int16 ExternalLink::getCalcSheetIndex( sal_Int32 nTabId ) const
724 {
725     OSL_ENSURE( meLinkType == LINKTYPE_INTERNAL, "ExternalLink::getCalcSheetIndex - invalid link type" );
726     OSL_ENSURE( (nTabId == 0) || (getFilterType() == FILTER_OOXML) || (getBiff() == BIFF8),
727         "ExternalLink::getCalcSheetIndex - invalid sheet index" );
728     return ContainerHelper::getVectorElement( maCalcSheets, nTabId, -1 );
729 }
730 
731 sal_Int32 ExternalLink::getDocumentLinkIndex() const
732 {
733     OSL_ENSURE( meLinkType == LINKTYPE_EXTERNAL, "ExternalLink::getDocumentLinkIndex - invalid link type" );
734     return mxDocLink.is() ? mxDocLink->getTokenIndex() : -1;
735 }
736 
737 sal_Int32 ExternalLink::getSheetCacheIndex( sal_Int32 nTabId ) const
738 {
739     OSL_ENSURE( meLinkType == LINKTYPE_EXTERNAL, "ExternalLink::getSheetCacheIndex - invalid link type" );
740     OSL_ENSURE( (nTabId == 0) || (getFilterType() == FILTER_OOXML) || (getBiff() == BIFF8),
741         "ExternalLink::getSheetCacheIndex - invalid sheet index" );
742     return ContainerHelper::getVectorElement( maSheetCaches, nTabId, -1 );
743 }
744 
745 Reference< XExternalSheetCache > ExternalLink::getSheetCache( sal_Int32 nTabId ) const
746 {
747     sal_Int32 nCacheIdx = getSheetCacheIndex( nTabId );
748     if( mxDocLink.is() && (nCacheIdx >= 0) ) try
749     {
750         // existing mxDocLink implies that this is an external link
751         Reference< XExternalSheetCache > xSheetCache( mxDocLink->getByIndex( nCacheIdx ), UNO_QUERY_THROW );
752         return xSheetCache;
753     }
754     catch( Exception& )
755     {
756     }
757     return 0;
758 }
759 
760 void ExternalLink::getSheetRange( LinkSheetRange& orSheetRange, sal_Int32 nTabId1, sal_Int32 nTabId2 ) const
761 {
762     switch( meLinkType )
763     {
764         case LINKTYPE_SAME:
765             orSheetRange.setSameSheet();
766         break;
767 
768         case LINKTYPE_SELF:
769         case LINKTYPE_INTERNAL:
770             orSheetRange.setRange( nTabId1, nTabId2 );
771         break;
772 
773         case LINKTYPE_EXTERNAL:
774         {
775             sal_Int32 nDocLinkIdx = getDocumentLinkIndex();
776             switch( getFilterType() )
777             {
778                 case FILTER_OOXML:
779                     // BIFF12: passed indexes point into sheet list of EXTSHEETLIST
780                     orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
781                 break;
782                 case FILTER_BIFF:
783                     switch( getBiff() )
784                     {
785                         case BIFF2:
786                         case BIFF3:
787                         case BIFF4:
788                             orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
789                         break;
790                         case BIFF5:
791                             // BIFF5: first sheet from this external link, last sheet is passed in nTabId2
792                             if( const ExternalLink* pExtLink2 = getExternalLinks().getExternalLink( nTabId2 ).get() )
793                                 if( (pExtLink2->getLinkType() == LINKTYPE_EXTERNAL) && (maTargetUrl == pExtLink2->getTargetUrl()) )
794                                     orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex(), pExtLink2->getSheetCacheIndex() );
795                         break;
796                         case BIFF8:
797                             // BIFF8: passed indexes point into sheet list of EXTERNALBOOK
798                             orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
799                         break;
800                         case BIFF_UNKNOWN: break;
801                     }
802                 break;
803                 case FILTER_UNKNOWN: break;
804             }
805         }
806         break;
807 
808         default:
809             // unsupported/unexpected link type: #REF! error
810             orSheetRange.setDeleted();
811     }
812 }
813 
814 ExternalNameRef ExternalLink::getNameByIndex( sal_Int32 nIndex ) const
815 {
816     return maExtNames.get( nIndex );
817 }
818 
819 // private --------------------------------------------------------------------
820 
821 #define OOX_TARGETTYPE_EXTLINK      CREATE_OFFICEDOC_RELATION_TYPE( "externalLinkPath" )
822 #define OOX_TARGETTYPE_LIBRARY      CREATE_MSOFFICE_RELATION_TYPE( "xlExternalLinkPath/xlLibrary" )
823 
824 void ExternalLink::setExternalTargetUrl( const OUString& rTargetUrl, const OUString& rTargetType )
825 {
826     meLinkType = LINKTYPE_UNKNOWN;
827     if( rTargetType == OOX_TARGETTYPE_EXTLINK )
828     {
829         maTargetUrl = getBaseFilter().getAbsoluteUrl( rTargetUrl );
830         if( maTargetUrl.getLength() > 0 )
831             meLinkType = LINKTYPE_EXTERNAL;
832     }
833     else if( rTargetType == OOX_TARGETTYPE_LIBRARY )
834     {
835         meLinkType = LINKTYPE_LIBRARY;
836         meFuncLibType = getFormulaParser().getFuncLibTypeFromLibraryName( rTargetUrl );
837     }
838     OSL_ENSURE( meLinkType != LINKTYPE_UNKNOWN, "ExternalLink::setExternalTargetUrl - empty target URL or unknown target type" );
839 
840     // create the external document link API object that will contain the sheet caches
841     if( meLinkType == LINKTYPE_EXTERNAL ) try
842     {
843         PropertySet aDocProps( getDocument() );
844         Reference< XExternalDocLinks > xDocLinks( aDocProps.getAnyProperty( PROP_ExternalDocLinks ), UNO_QUERY_THROW );
845         mxDocLink = xDocLinks->addDocLink( maTargetUrl );
846     }
847     catch( Exception& )
848     {
849     }
850 }
851 
852 void ExternalLink::setDdeOleTargetUrl( const OUString& rClassName, const OUString& rTargetUrl, ExternalLinkType eLinkType )
853 {
854     maClassName = rClassName;
855     maTargetUrl = rTargetUrl;
856     meLinkType = ((maClassName.getLength() > 0) && (maTargetUrl.getLength() > 0)) ? eLinkType : LINKTYPE_UNKNOWN;
857     OSL_ENSURE( meLinkType == eLinkType, "ExternalLink::setDdeOleTargetUrl - missing classname or target" );
858 }
859 
860 void ExternalLink::parseExternalReference( const Relations& rRelations, const OUString& rRelId )
861 {
862     if( const Relation* pRelation = rRelations.getRelationFromRelId( rRelId ) )
863         setExternalTargetUrl( pRelation->maTarget, pRelation->maType );
864 }
865 
866 OUString ExternalLink::parseBiffTargetUrl( const OUString& rBiffTargetUrl )
867 {
868     meLinkType = LINKTYPE_UNKNOWN;
869 
870     OUString aClassName, aTargetUrl, aSheetName;
871     switch( getAddressConverter().parseBiffTargetUrl( aClassName, aTargetUrl, aSheetName, rBiffTargetUrl ) )
872     {
873         case BIFF_TARGETTYPE_URL:
874             if( aTargetUrl.getLength() == 0 )
875             {
876                 meLinkType = (aSheetName.getLength() > 0) ? LINKTYPE_INTERNAL : LINKTYPE_SELF;
877             }
878             else if( (aTargetUrl.getLength() == 1) && (aTargetUrl[ 0 ] == ':') )
879             {
880                 if( getBiff() >= BIFF4 )
881                     meLinkType = LINKTYPE_ANALYSIS;
882             }
883             else if( (aTargetUrl.getLength() > 1) || (aTargetUrl[ 0 ] != ' ') )
884             {
885                 setExternalTargetUrl( aTargetUrl, OOX_TARGETTYPE_EXTLINK );
886             }
887         break;
888 
889         case BIFF_TARGETTYPE_SAMESHEET:
890             OSL_ENSURE( (aTargetUrl.getLength() == 0) && (aSheetName.getLength() == 0), "ExternalLink::parseBiffTargetUrl - unexpected target or sheet name" );
891             meLinkType = LINKTYPE_SAME;
892         break;
893 
894         case BIFF_TARGETTYPE_LIBRARY:
895             OSL_ENSURE( aSheetName.getLength() == 0, "ExternalLink::parseBiffTargetUrl - unexpected sheet name" );
896             setExternalTargetUrl( aTargetUrl, OOX_TARGETTYPE_LIBRARY );
897         break;
898 
899         case BIFF_TARGETTYPE_DDE_OLE:
900             setDdeOleTargetUrl( aClassName, aTargetUrl, LINKTYPE_MAYBE_DDE_OLE );
901         break;
902 
903         case BIFF_TARGETTYPE_UNKNOWN:
904         break;
905     }
906     return aSheetName;
907 }
908 
909 void ExternalLink::insertExternalSheet( const OUString& rSheetName )
910 {
911     OSL_ENSURE( rSheetName.getLength() > 0, "ExternalLink::insertExternalSheet - empty sheet name" );
912     if( mxDocLink.is() )
913     {
914         Reference< XExternalSheetCache > xSheetCache = mxDocLink->addSheetCache( rSheetName, false );
915         sal_Int32 nCacheIdx = xSheetCache.is() ? xSheetCache->getTokenIndex() : -1;
916         maSheetCaches.push_back( nCacheIdx );
917     }
918 }
919 
920 ExternalNameRef ExternalLink::createExternalName()
921 {
922     ExternalNameRef xExtName( new ExternalName( *this ) );
923     maExtNames.push_back( xExtName );
924     return xExtName;
925 }
926 
927 // ============================================================================
928 
929 RefSheetsModel::RefSheetsModel() :
930     mnExtRefId( -1 ),
931     mnTabId1( -1 ),
932     mnTabId2( -1 )
933 {
934 }
935 
936 void RefSheetsModel::readBiff12Data( SequenceInputStream& rStrm )
937 {
938     rStrm >> mnExtRefId >> mnTabId1 >> mnTabId2;
939 }
940 
941 void RefSheetsModel::readBiff8Data( BiffInputStream& rStrm )
942 {
943     mnExtRefId = rStrm.readuInt16();
944     mnTabId1 = rStrm.readInt16();
945     mnTabId2 = rStrm.readInt16();
946 }
947 
948 // ----------------------------------------------------------------------------
949 
950 ExternalLinkBuffer::ExternalLinkBuffer( const WorkbookHelper& rHelper ) :
951     WorkbookHelper( rHelper ),
952     mxSelfRef( new ExternalLink( rHelper ) ),
953     mbUseRefSheets( false )
954 {
955     mxSelfRef->setSelfLinkType();
956 }
957 
958 ExternalLinkRef ExternalLinkBuffer::importExternalReference( const AttributeList& rAttribs )
959 {
960     ExternalLinkRef xExtLink = createExternalLink();
961     xExtLink->importExternalReference( rAttribs );
962     maExtLinks.push_back( xExtLink );
963     return xExtLink;
964 }
965 
966 ExternalLinkRef ExternalLinkBuffer::importExternalRef( SequenceInputStream& rStrm )
967 {
968     mbUseRefSheets = true;
969     ExternalLinkRef xExtLink = createExternalLink();
970     xExtLink->importExternalRef( rStrm );
971     maExtLinks.push_back( xExtLink );
972     return xExtLink;
973 }
974 
975 void ExternalLinkBuffer::importExternalSelf( SequenceInputStream& rStrm )
976 {
977     mbUseRefSheets = true;
978     createExternalLink()->importExternalSelf( rStrm );
979 }
980 
981 void ExternalLinkBuffer::importExternalSame( SequenceInputStream& rStrm )
982 {
983     mbUseRefSheets = true;
984     createExternalLink()->importExternalSame( rStrm );
985 }
986 
987 void ExternalLinkBuffer::importExternalAddin( SequenceInputStream& rStrm )
988 {
989     mbUseRefSheets = true;
990     createExternalLink()->importExternalAddin( rStrm );
991 }
992 
993 void ExternalLinkBuffer::importExternalSheets( SequenceInputStream& rStrm )
994 {
995     OSL_ENSURE( mbUseRefSheets, "ExternalLinkBuffer::importExternalSheets - missing EXTERNALREFS records" );
996     mbUseRefSheets = true;
997     OSL_ENSURE( maRefSheets.empty(), "ExternalLinkBuffer::importExternalSheets - multiple EXTERNALSHEETS records" );
998     maRefSheets.clear();
999     sal_Int32 nRefCount;
1000     rStrm >> nRefCount;
1001     size_t nMaxCount = getLimitedValue< size_t, sal_Int64 >( nRefCount, 0, rStrm.getRemaining() / 12 );
1002     maRefSheets.reserve( nMaxCount );
1003     for( size_t nRefId = 0; !rStrm.isEof() && (nRefId < nMaxCount); ++nRefId )
1004     {
1005         RefSheetsModel aRefSheets;
1006         aRefSheets.readBiff12Data( rStrm );
1007         maRefSheets.push_back( aRefSheets );
1008     }
1009 }
1010 
1011 ExternalLinkRef ExternalLinkBuffer::importExternSheet( BiffInputStream& rStrm )
1012 {
1013     OSL_ENSURE( getBiff() <= BIFF5, "ExternalLinkBuffer::importExternSheet - wrong BIFF version" );
1014     ExternalLinkRef xExtLink = createExternalLink();
1015     xExtLink->importExternSheet( rStrm );
1016     return xExtLink;
1017 }
1018 
1019 ExternalLinkRef ExternalLinkBuffer::importExternalBook( BiffInputStream& rStrm )
1020 {
1021     ExternalLinkRef xExtLink = createExternalLink();
1022     xExtLink->importExternalBook( rStrm );
1023     return xExtLink;
1024 }
1025 
1026 void ExternalLinkBuffer::importExternalName( BiffInputStream& rStrm )
1027 {
1028     if( !maLinks.empty() )
1029         maLinks.back()->importExternalName( rStrm );
1030 }
1031 
1032 void ExternalLinkBuffer::importExternSheet8( BiffInputStream& rStrm )
1033 {
1034     OSL_ENSURE( getBiff() == BIFF8, "ExternalLinkBuffer::importExternSheet8 - wrong BIFF version" );
1035 
1036     sal_uInt16 nRefCount;
1037     rStrm >> nRefCount;
1038     OSL_ENSURE( static_cast< sal_Int64 >( nRefCount * 6 ) == rStrm.getRemaining(), "ExternalLinkBuffer::importExternSheet8 - invalid count" );
1039     nRefCount = static_cast< sal_uInt16 >( ::std::min< sal_Int64 >( nRefCount, rStrm.getRemaining() / 6 ) );
1040 
1041     /*  #i104057# A weird external XLS generator writes multiple EXTERNSHEET
1042         records instead of only one as expected. Surprisingly, Excel seems to
1043         insert the entries of the second record before the entries of the first
1044         record. */
1045     maRefSheets.insert( maRefSheets.begin(), nRefCount, RefSheetsModel() );
1046     for( RefSheetsModelVec::iterator aIt = maRefSheets.begin(), aEnd = aIt + nRefCount; !rStrm.isEof() && (aIt != aEnd); ++aIt )
1047         aIt->readBiff8Data( rStrm );
1048 }
1049 
1050 Sequence< ExternalLinkInfo > ExternalLinkBuffer::getLinkInfos() const
1051 {
1052     ::std::vector< ExternalLinkInfo > aLinkInfos;
1053     // XML formula parser also used in BIFF12 documents, e.g. replacement formulas in unsupported conditional formattings
1054     OSL_ENSURE( getFilterType() == FILTER_OOXML, "ExternalLinkBuffer::getLinkInfos - unexpected file format" );
1055     // add entry for implicit index 0 (self reference to this document)
1056     aLinkInfos.push_back( mxSelfRef->getLinkInfo() );
1057     for( ExternalLinkVec::const_iterator aIt = maExtLinks.begin(), aEnd = maExtLinks.end(); aIt != aEnd; ++aIt )
1058         aLinkInfos.push_back( (*aIt)->getLinkInfo() );
1059     return ContainerHelper::vectorToSequence( aLinkInfos );
1060 }
1061 
1062 ExternalLinkRef ExternalLinkBuffer::getExternalLink( sal_Int32 nRefId, bool bUseRefSheets ) const
1063 {
1064     ExternalLinkRef xExtLink;
1065     switch( getFilterType() )
1066     {
1067         case FILTER_OOXML:
1068             // OOXML: 0 = this document, otherwise one-based index into link list
1069             if( !bUseRefSheets || !mbUseRefSheets )
1070                 xExtLink = (nRefId == 0) ? mxSelfRef : maLinks.get( nRefId - 1 );
1071             // BIFF12: zero-based index into ref-sheets list
1072             else if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
1073                 xExtLink = maLinks.get( pRefSheets->mnExtRefId );
1074         break;
1075         case FILTER_BIFF:
1076             switch( getBiff() )
1077             {
1078                 case BIFF2:
1079                 case BIFF3:
1080                 case BIFF4:
1081                     // one-based index to EXTERNSHEET records
1082                     xExtLink = maLinks.get( nRefId - 1 );
1083                 break;
1084                 case BIFF5:
1085                     if( nRefId < 0 )
1086                     {
1087                         // internal links in formula tokens have negative index
1088                         xExtLink = maLinks.get( -nRefId - 1 );
1089                         if( xExtLink.get() && !xExtLink->isInternalLink() )
1090                             xExtLink.reset();
1091                     }
1092                     else
1093                     {
1094                         // one-based index to EXTERNSHEET records
1095                         xExtLink = maLinks.get( nRefId - 1 );
1096                     }
1097                 break;
1098                 case BIFF8:
1099                     // zero-based index into REF list in EXTERNSHEET record
1100                     if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
1101                         xExtLink = maLinks.get( pRefSheets->mnExtRefId );
1102                 break;
1103                 case BIFF_UNKNOWN: break;
1104             }
1105         break;
1106         case FILTER_UNKNOWN: break;
1107     }
1108     return xExtLink;
1109 }
1110 
1111 LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId, sal_Int16 nTabId1, sal_Int16 nTabId2 ) const
1112 {
1113     OSL_ENSURE( getBiff() <= BIFF5, "ExternalLinkBuffer::getSheetRange - wrong BIFF version" );
1114     LinkSheetRange aSheetRange;
1115     if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() )
1116         pExtLink->getSheetRange( aSheetRange, nTabId1, nTabId2 );
1117     return aSheetRange;
1118 }
1119 
1120 LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId ) const
1121 {
1122     OSL_ENSURE( ((getFilterType() == FILTER_OOXML) && mbUseRefSheets) || (getBiff() == BIFF8), "ExternalLinkBuffer::getSheetRange - wrong BIFF version" );
1123     LinkSheetRange aSheetRange;
1124     if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() )
1125         if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
1126             pExtLink->getSheetRange( aSheetRange, pRefSheets->mnTabId1, pRefSheets->mnTabId2 );
1127     return aSheetRange;
1128 }
1129 
1130 // private --------------------------------------------------------------------
1131 
1132 ExternalLinkRef ExternalLinkBuffer::createExternalLink()
1133 {
1134     ExternalLinkRef xExtLink( new ExternalLink( *this ) );
1135     maLinks.push_back( xExtLink );
1136     return xExtLink;
1137 }
1138 
1139 const RefSheetsModel* ExternalLinkBuffer::getRefSheets( sal_Int32 nRefId ) const
1140 {
1141     return ((0 <= nRefId) && (static_cast< size_t >( nRefId ) < maRefSheets.size())) ?
1142         &maRefSheets[ static_cast< size_t >( nRefId ) ] : 0;
1143 }
1144 
1145 // ============================================================================
1146 
1147 } // namespace xls
1148 } // namespace oox
1149