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