1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "oox/xls/externallinkfragment.hxx"
25 
26 #include <com/sun/star/sheet/XExternalSheetCache.hpp>
27 #include "oox/helper/attributelist.hxx"
28 #include "oox/xls/biffinputstream.hxx"
29 #include "oox/xls/defnamesbuffer.hxx"
30 #include "oox/xls/sheetdatacontext.hxx"
31 #include "oox/xls/unitconverter.hxx"
32 
33 namespace oox {
34 namespace xls {
35 
36 // ============================================================================
37 
38 using namespace ::com::sun::star::sheet;
39 using namespace ::com::sun::star::table;
40 using namespace ::com::sun::star::uno;
41 using namespace ::oox::core;
42 
43 using ::rtl::OUString;
44 
45 // ============================================================================
46 // ============================================================================
47 
ExternalSheetDataContext(WorkbookFragmentBase & rFragment,const Reference<XExternalSheetCache> & rxSheetCache)48 ExternalSheetDataContext::ExternalSheetDataContext(
49         WorkbookFragmentBase& rFragment, const Reference< XExternalSheetCache >& rxSheetCache ) :
50     WorkbookContextBase( rFragment ),
51     mxSheetCache( rxSheetCache )
52 {
53     OSL_ENSURE( mxSheetCache.is(), "ExternalSheetDataContext::ExternalSheetDataContext - missing sheet cache" );
54 }
55 
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)56 ContextHandlerRef ExternalSheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
57 {
58     switch( getCurrentElement() )
59     {
60         case XLS_TOKEN( sheetData ):
61             if( nElement == XLS_TOKEN( row ) ) return this;
62         break;
63         case XLS_TOKEN( row ):
64             if( nElement == XLS_TOKEN( cell ) ) { importCell( rAttribs ); return this; }
65         break;
66         case XLS_TOKEN( cell ):
67             if( nElement == XLS_TOKEN( v ) ) return this;   // collect characters in onCharacters()
68         break;
69     }
70     return 0;
71 }
72 
onCharacters(const OUString & rChars)73 void ExternalSheetDataContext::onCharacters( const OUString& rChars )
74 {
75     if( isCurrentElement( XLS_TOKEN( v ) ) )
76     {
77         switch( mnCurrType )
78         {
79             case XML_b:
80             case XML_n:
81                 setCellValue( Any( rChars.toDouble() ) );
82             break;
83             case XML_e:
84                 setCellValue( Any( BiffHelper::calcDoubleFromError( getUnitConverter().calcBiffErrorCode( rChars ) ) ) );
85             break;
86             case XML_str:
87                 setCellValue( Any( rChars ) );
88             break;
89         }
90         mnCurrType = XML_TOKEN_INVALID;
91     }
92 }
93 
onCreateRecordContext(sal_Int32 nRecId,SequenceInputStream & rStrm)94 ContextHandlerRef ExternalSheetDataContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
95 {
96     switch( getCurrentElement() )
97     {
98         case BIFF12_ID_EXTSHEETDATA:
99             if( nRecId == BIFF12_ID_EXTROW ) { maCurrPos.Row = rStrm.readInt32(); return this; }
100         break;
101         case BIFF12_ID_EXTROW:
102             switch( nRecId )
103             {
104                 case BIFF12_ID_EXTCELL_BLANK:   importExtCellBlank( rStrm );    break;
105                 case BIFF12_ID_EXTCELL_BOOL:    importExtCellBool( rStrm );     break;
106                 case BIFF12_ID_EXTCELL_DOUBLE:  importExtCellDouble( rStrm );   break;
107                 case BIFF12_ID_EXTCELL_ERROR:   importExtCellError( rStrm );    break;
108                 case BIFF12_ID_EXTCELL_STRING:  importExtCellString( rStrm );   break;
109             }
110         break;
111     }
112     return 0;
113 }
114 
115 // private --------------------------------------------------------------------
116 
importCell(const AttributeList & rAttribs)117 void ExternalSheetDataContext::importCell( const AttributeList& rAttribs )
118 {
119     if( getAddressConverter().convertToCellAddress( maCurrPos, rAttribs.getString( XML_r, OUString() ), 0, false ) )
120         mnCurrType = rAttribs.getToken( XML_t, XML_n );
121     else
122         mnCurrType = XML_TOKEN_INVALID;
123 }
124 
importExtCellBlank(SequenceInputStream & rStrm)125 void ExternalSheetDataContext::importExtCellBlank( SequenceInputStream& rStrm )
126 {
127     maCurrPos.Column = rStrm.readInt32();
128     setCellValue( Any( OUString() ) );
129 }
130 
importExtCellBool(SequenceInputStream & rStrm)131 void ExternalSheetDataContext::importExtCellBool( SequenceInputStream& rStrm )
132 {
133     maCurrPos.Column = rStrm.readInt32();
134     double fValue = (rStrm.readuInt8() == 0) ? 0.0 : 1.0;
135     setCellValue( Any( fValue ) );
136 }
137 
importExtCellDouble(SequenceInputStream & rStrm)138 void ExternalSheetDataContext::importExtCellDouble( SequenceInputStream& rStrm )
139 {
140     maCurrPos.Column = rStrm.readInt32();
141     setCellValue( Any( rStrm.readDouble() ) );
142 }
143 
importExtCellError(SequenceInputStream & rStrm)144 void ExternalSheetDataContext::importExtCellError( SequenceInputStream& rStrm )
145 {
146     maCurrPos.Column = rStrm.readInt32();
147     setCellValue( Any( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) ) );
148 }
149 
importExtCellString(SequenceInputStream & rStrm)150 void ExternalSheetDataContext::importExtCellString( SequenceInputStream& rStrm )
151 {
152     maCurrPos.Column = rStrm.readInt32();
153     setCellValue( Any( BiffHelper::readString( rStrm ) ) );
154 }
155 
setCellValue(const Any & rValue)156 void ExternalSheetDataContext::setCellValue( const Any& rValue )
157 {
158     if( mxSheetCache.is() && getAddressConverter().checkCellAddress( maCurrPos, false ) ) try
159     {
160         mxSheetCache->setCellValue( maCurrPos.Column, maCurrPos.Row, rValue );
161     }
162     catch( Exception& )
163     {
164     }
165 }
166 
167 // ============================================================================
168 
ExternalLinkFragment(const WorkbookHelper & rHelper,const OUString & rFragmentPath,ExternalLink & rExtLink)169 ExternalLinkFragment::ExternalLinkFragment( const WorkbookHelper& rHelper,
170         const OUString& rFragmentPath, ExternalLink& rExtLink ) :
171     WorkbookFragmentBase( rHelper, rFragmentPath ),
172     mrExtLink( rExtLink ),
173     mnResultType( XML_TOKEN_INVALID )
174 {
175 }
176 
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)177 ContextHandlerRef ExternalLinkFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
178 {
179     switch( getCurrentElement() )
180     {
181         case XML_ROOT_CONTEXT:
182             if( nElement == XLS_TOKEN( externalLink ) ) return this;
183         break;
184 
185         case XLS_TOKEN( externalLink ):
186             switch( nElement )
187             {
188                 case XLS_TOKEN( externalBook ): mrExtLink.importExternalBook( getRelations(), rAttribs );   return this;
189                 case XLS_TOKEN( ddeLink ):      mrExtLink.importDdeLink( rAttribs );                        return this;
190                 case XLS_TOKEN( oleLink ):      mrExtLink.importOleLink( getRelations(), rAttribs );        return this;
191             }
192         break;
193 
194         case XLS_TOKEN( externalBook ):
195             switch( nElement )
196             {
197                 case XLS_TOKEN( sheetNames ):
198                 case XLS_TOKEN( definedNames ):
199                 case XLS_TOKEN( sheetDataSet ): return this;
200             }
201         break;
202 
203         case XLS_TOKEN( sheetNames ):
204             if( nElement == XLS_TOKEN( sheetName ) ) mrExtLink.importSheetName( rAttribs );
205         break;
206         case XLS_TOKEN( definedNames ):
207             if( nElement == XLS_TOKEN( definedName ) ) mrExtLink.importDefinedName( rAttribs );
208         break;
209         case XLS_TOKEN( sheetDataSet ):
210             if( (nElement == XLS_TOKEN( sheetData )) && (mrExtLink.getLinkType() == LINKTYPE_EXTERNAL) )
211                 return createSheetDataContext( rAttribs.getInteger( XML_sheetId, -1 ) );
212         break;
213 
214         case XLS_TOKEN( ddeLink ):
215             if( nElement == XLS_TOKEN( ddeItems ) ) return this;
216         break;
217         case XLS_TOKEN( ddeItems ):
218             if( nElement == XLS_TOKEN( ddeItem ) )
219             {
220                 mxExtName = mrExtLink.importDdeItem( rAttribs );
221                 return this;
222             }
223         break;
224         case XLS_TOKEN( ddeItem ):
225             if( nElement == XLS_TOKEN( values ) )
226             {
227                 if( mxExtName.get() ) mxExtName->importValues( rAttribs );
228                 return this;
229             }
230         break;
231         case XLS_TOKEN( values ):
232             if( nElement == XLS_TOKEN( value ) )
233             {
234                 mnResultType = rAttribs.getToken( XML_t, XML_n );
235                 return this;
236             }
237         break;
238         case XLS_TOKEN( value ):
239             if( nElement == XLS_TOKEN( val ) ) return this; // collect value in onCharacters()
240         break;
241 
242         case XLS_TOKEN( oleLink ):
243             if( nElement == XLS_TOKEN( oleItems ) ) return this;
244         break;
245         case XLS_TOKEN( oleItems ):
246             if( nElement == XLS_TOKEN( oleItem ) ) mxExtName = mrExtLink.importOleItem( rAttribs );
247         break;
248     }
249     return 0;
250 }
251 
onCharacters(const OUString & rChars)252 void ExternalLinkFragment::onCharacters( const OUString& rChars )
253 {
254     if( isCurrentElement( XLS_TOKEN( val ) ) )
255         maResultValue = rChars;
256 }
257 
onEndElement()258 void ExternalLinkFragment::onEndElement()
259 {
260     if( isCurrentElement( XLS_TOKEN( value ) ) && mxExtName.get() ) switch( mnResultType )
261     {
262         case XML_b:
263             mxExtName->appendResultValue( maResultValue.toDouble() );
264         break;
265         case XML_e:
266             mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( getUnitConverter().calcBiffErrorCode( maResultValue ) ) );
267         break;
268         case XML_n:
269             mxExtName->appendResultValue( maResultValue.toDouble() );
270         break;
271         case XML_str:
272             mxExtName->appendResultValue( maResultValue );
273         break;
274         default:
275             mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) );
276     }
277 }
278 
onCreateRecordContext(sal_Int32 nRecId,SequenceInputStream & rStrm)279 ContextHandlerRef ExternalLinkFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
280 {
281     switch( getCurrentElement() )
282     {
283         case XML_ROOT_CONTEXT:
284             if( nRecId == BIFF12_ID_EXTERNALBOOK )
285             {
286                 mrExtLink.importExternalBook( getRelations(), rStrm );
287                 return this;
288             }
289         break;
290 
291         case BIFF12_ID_EXTERNALBOOK:
292             switch( nRecId )
293             {
294                 case BIFF12_ID_EXTSHEETDATA:
295                     if( mrExtLink.getLinkType() == LINKTYPE_EXTERNAL )
296                         return createSheetDataContext( rStrm.readInt32() );
297                 break;
298 
299                 case BIFF12_ID_EXTSHEETNAMES:       mrExtLink.importExtSheetNames( rStrm );                             break;
300                 case BIFF12_ID_EXTERNALNAME:        mxExtName = mrExtLink.importExternalName( rStrm );                  return this;
301             }
302         break;
303 
304         case BIFF12_ID_EXTERNALNAME:
305             switch( nRecId )
306             {
307                 case BIFF12_ID_EXTERNALNAMEFLAGS:   if( mxExtName.get() ) mxExtName->importExternalNameFlags( rStrm );  break;
308                 case BIFF12_ID_DDEITEMVALUES:       if( mxExtName.get() ) mxExtName->importDdeItemValues( rStrm );      return this;
309             }
310         break;
311 
312         case BIFF12_ID_DDEITEMVALUES:
313             switch( nRecId )
314             {
315                 case BIFF12_ID_DDEITEM_BOOL:        if( mxExtName.get() ) mxExtName->importDdeItemBool( rStrm );        break;
316                 case BIFF12_ID_DDEITEM_DOUBLE:      if( mxExtName.get() ) mxExtName->importDdeItemDouble( rStrm );      break;
317                 case BIFF12_ID_DDEITEM_ERROR:       if( mxExtName.get() ) mxExtName->importDdeItemError( rStrm );       break;
318                 case BIFF12_ID_DDEITEM_STRING:      if( mxExtName.get() ) mxExtName->importDdeItemString( rStrm );      break;
319             }
320         break;
321     }
322     return 0;
323 }
324 
createSheetDataContext(sal_Int32 nSheetId)325 ContextHandlerRef ExternalLinkFragment::createSheetDataContext( sal_Int32 nSheetId )
326 {
327     return new ExternalSheetDataContext( *this, mrExtLink.getSheetCache( nSheetId ) );
328 }
329 
getRecordInfos() const330 const RecordInfo* ExternalLinkFragment::getRecordInfos() const
331 {
332     static const RecordInfo spRecInfos[] =
333     {
334         { BIFF12_ID_DDEITEMVALUES,  BIFF12_ID_DDEITEMVALUES + 1     },
335         { BIFF12_ID_EXTERNALBOOK,   BIFF12_ID_EXTERNALBOOK + 228    },
336         { BIFF12_ID_EXTERNALNAME,   BIFF12_ID_EXTERNALNAME + 10     },
337         { BIFF12_ID_EXTROW,         -1                              },
338         { BIFF12_ID_EXTSHEETDATA,   BIFF12_ID_EXTSHEETDATA + 1      },
339         { -1,                       -1                              }
340     };
341     return spRecInfos;
342 }
343 
344 // ============================================================================
345 // ============================================================================
346 
BiffExternalSheetDataContext(const WorkbookHelper & rHelper,bool bImportDefNames)347 BiffExternalSheetDataContext::BiffExternalSheetDataContext( const WorkbookHelper& rHelper, bool bImportDefNames ) :
348     BiffWorkbookContextBase( rHelper ),
349     mbImportDefNames( bImportDefNames )
350 {
351 }
352 
~BiffExternalSheetDataContext()353 BiffExternalSheetDataContext::~BiffExternalSheetDataContext()
354 {
355 }
356 
importRecord(BiffInputStream & rStrm)357 void BiffExternalSheetDataContext::importRecord( BiffInputStream& rStrm )
358 {
359     sal_uInt16 nRecId = rStrm.getRecId();
360     switch( getBiff() )
361     {
362         case BIFF2: switch( nRecId )
363         {
364             case BIFF2_ID_EXTERNALNAME: importExternalName( rStrm );    break;
365             case BIFF_ID_EXTERNSHEET:   importExternSheet( rStrm );     break;
366             case BIFF2_ID_DEFINEDNAME:  importDefinedName( rStrm );     break;
367         }
368         break;
369         case BIFF3: switch( nRecId )
370         {
371             case BIFF_ID_CRN:           importCrn( rStrm );             break;
372             case BIFF3_ID_EXTERNALNAME: importExternalName( rStrm );    break;
373             case BIFF_ID_EXTERNSHEET:   importExternSheet( rStrm );     break;
374             case BIFF3_ID_DEFINEDNAME:  importDefinedName( rStrm );     break;
375             case BIFF_ID_XCT:           importXct( rStrm );             break;
376         }
377         break;
378         case BIFF4: switch( nRecId )
379         {
380             case BIFF_ID_CRN:           importCrn( rStrm );             break;
381             case BIFF3_ID_EXTERNALNAME: importExternalName( rStrm );    break;
382             case BIFF_ID_EXTERNSHEET:   importExternSheet( rStrm );     break;
383             case BIFF3_ID_DEFINEDNAME:  importDefinedName( rStrm );     break;
384             case BIFF_ID_XCT:           importXct( rStrm );             break;
385         }
386         break;
387         case BIFF5: switch( nRecId )
388         {
389             case BIFF_ID_CRN:           importCrn( rStrm );             break;
390             case BIFF5_ID_EXTERNALNAME: importExternalName( rStrm );    break;
391             case BIFF_ID_EXTERNSHEET:   importExternSheet( rStrm );     break;
392             case BIFF5_ID_DEFINEDNAME:  importDefinedName( rStrm );     break;
393             case BIFF_ID_XCT:           importXct( rStrm );             break;
394         }
395         break;
396         case BIFF8: switch( nRecId )
397         {
398             case BIFF_ID_CRN:           importCrn( rStrm );             break;
399             case BIFF_ID_EXTERNALBOOK:  importExternalBook( rStrm );    break;
400             case BIFF5_ID_EXTERNALNAME: importExternalName( rStrm );    break;
401             case BIFF_ID_EXTERNSHEET:   importExternSheet( rStrm );     break;
402             case BIFF5_ID_DEFINEDNAME:  importDefinedName( rStrm );     break;
403             case BIFF_ID_XCT:           importXct( rStrm );             break;
404         }
405         break;
406         case BIFF_UNKNOWN: break;
407     }
408 }
409 
410 // private --------------------------------------------------------------------
411 
importExternSheet(BiffInputStream & rStrm)412 void BiffExternalSheetDataContext::importExternSheet( BiffInputStream& rStrm )
413 {
414     mxSheetCache.clear();
415     if( getBiff() == BIFF8 )
416         getExternalLinks().importExternSheet8( rStrm );
417     else
418         mxExtLink = getExternalLinks().importExternSheet( rStrm );
419 }
420 
importExternalBook(BiffInputStream & rStrm)421 void BiffExternalSheetDataContext::importExternalBook( BiffInputStream& rStrm )
422 {
423     mxSheetCache.clear();
424     mxExtLink = getExternalLinks().importExternalBook( rStrm );
425 }
426 
importExternalName(BiffInputStream & rStrm)427 void BiffExternalSheetDataContext::importExternalName( BiffInputStream& rStrm )
428 {
429     if( mxExtLink.get() )
430         mxExtLink->importExternalName( rStrm );
431 }
432 
importXct(BiffInputStream & rStrm)433 void BiffExternalSheetDataContext::importXct( BiffInputStream& rStrm )
434 {
435     mxSheetCache.clear();
436     if( mxExtLink.get() && (mxExtLink->getLinkType() == LINKTYPE_EXTERNAL) )
437     {
438         switch( getBiff() )
439         {
440             case BIFF2:
441             break;
442             case BIFF3:
443             case BIFF4:
444             case BIFF5:
445                 mxSheetCache = mxExtLink->getSheetCache( 0 );
446             break;
447             case BIFF8:
448                 rStrm.skip( 2 );
449                 mxSheetCache = mxExtLink->getSheetCache( rStrm.readInt16() );
450             break;
451             case BIFF_UNKNOWN:
452             break;
453         }
454     }
455 }
456 
importCrn(BiffInputStream & rStrm)457 void BiffExternalSheetDataContext::importCrn( BiffInputStream& rStrm )
458 {
459     if( !mxSheetCache.is() ) return;
460 
461     sal_uInt8 nCol2, nCol1;
462     sal_uInt16 nRow;
463     rStrm >> nCol2 >> nCol1 >> nRow;
464     bool bLoop = true;
465     for( BinAddress aBinAddr( nCol1, nRow ); bLoop && !rStrm.isEof() && (aBinAddr.mnCol <= nCol2); ++aBinAddr.mnCol )
466     {
467         switch( rStrm.readuInt8() )
468         {
469             case BIFF_DATATYPE_EMPTY:
470                 rStrm.skip( 8 );
471                 setCellValue( aBinAddr, Any( OUString() ) );
472             break;
473             case BIFF_DATATYPE_DOUBLE:
474                 setCellValue( aBinAddr, Any( rStrm.readDouble() ) );
475             break;
476             case BIFF_DATATYPE_STRING:
477             {
478                 OUString aText = (getBiff() == BIFF8) ? rStrm.readUniString() : rStrm.readByteStringUC( false, getTextEncoding() );
479                 setCellValue( aBinAddr, Any( aText ) );
480             }
481             break;
482             case BIFF_DATATYPE_BOOL:
483             {
484                 double fValue = (rStrm.readuInt8() == 0) ? 0.0 : 1.0;
485                 setCellValue( aBinAddr, Any( fValue ) );
486                 rStrm.skip( 7 );
487             }
488             break;
489             case BIFF_DATATYPE_ERROR:
490                 setCellValue( aBinAddr, Any( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) ) );
491                 rStrm.skip( 7 );
492             break;
493             default:
494                 OSL_ENSURE( false, "BiffExternalSheetDataContext::importCrn - unknown data type" );
495                 bLoop = false;
496         }
497     }
498 }
499 
importDefinedName(BiffInputStream & rStrm)500 void BiffExternalSheetDataContext::importDefinedName( BiffInputStream& rStrm )
501 {
502     if( mbImportDefNames )
503         getDefinedNames().importDefinedName( rStrm );
504 }
505 
setCellValue(const BinAddress & rBinAddr,const Any & rValue)506 void BiffExternalSheetDataContext::setCellValue( const BinAddress& rBinAddr, const Any& rValue )
507 {
508     CellAddress aCellPos;
509     if( mxSheetCache.is() && getAddressConverter().convertToCellAddress( aCellPos, rBinAddr, 0, false ) ) try
510     {
511         mxSheetCache->setCellValue( aCellPos.Column, aCellPos.Row, rValue );
512     }
513     catch( Exception& )
514     {
515     }
516 }
517 
518 // ============================================================================
519 
BiffExternalLinkFragment(const BiffWorkbookFragmentBase & rParent)520 BiffExternalLinkFragment::BiffExternalLinkFragment( const BiffWorkbookFragmentBase& rParent ) :
521     BiffWorkbookFragmentBase( rParent )
522 {
523 }
524 
importFragment()525 bool BiffExternalLinkFragment::importFragment()
526 {
527     // process all record in this sheet fragment
528     BiffExternalSheetDataContext aSheetContext( *this, false );
529     BiffInputStream& rStrm = getInputStream();
530     while( rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) )
531     {
532         if( BiffHelper::isBofRecord( rStrm ) )
533             skipFragment();  // skip unknown embedded fragments
534         else
535             aSheetContext.importRecord( rStrm );
536     }
537     return !rStrm.isEof() && (rStrm.getRecId() == BIFF_ID_EOF);
538 }
539 
540 // ============================================================================
541 // ============================================================================
542 
543 } // namespace xls
544 } // namespace oox
545