xref: /trunk/main/oox/source/xls/sheetdatabuffer.cxx (revision cdf0e10c)
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/sheetdatabuffer.hxx"
29 
30 #include <algorithm>
31 #include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
32 #include <com/sun/star/sheet/XCellRangeData.hpp>
33 #include <com/sun/star/sheet/XFormulaTokens.hpp>
34 #include <com/sun/star/sheet/XMultipleOperation.hpp>
35 #include <com/sun/star/table/XCell.hpp>
36 #include <com/sun/star/text/XText.hpp>
37 #include <com/sun/star/util/DateTime.hpp>
38 #include <com/sun/star/util/NumberFormat.hpp>
39 #include <com/sun/star/util/XMergeable.hpp>
40 #include <com/sun/star/util/XNumberFormatTypes.hpp>
41 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
42 #include <rtl/ustrbuf.hxx>
43 #include "oox/helper/containerhelper.hxx"
44 #include "oox/helper/propertymap.hxx"
45 #include "oox/helper/propertyset.hxx"
46 #include "oox/token/tokens.hxx"
47 #include "oox/xls/addressconverter.hxx"
48 #include "oox/xls/biffinputstream.hxx"
49 #include "oox/xls/formulaparser.hxx"
50 #include "oox/xls/sharedstringsbuffer.hxx"
51 #include "oox/xls/unitconverter.hxx"
52 
53 namespace oox {
54 namespace xls {
55 
56 // ============================================================================
57 
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::sheet;
60 using namespace ::com::sun::star::table;
61 using namespace ::com::sun::star::text;
62 using namespace ::com::sun::star::uno;
63 using namespace ::com::sun::star::util;
64 
65 using ::rtl::OUString;
66 using ::rtl::OUStringBuffer;
67 
68 // ============================================================================
69 
70 CellModel::CellModel() :
71     mnCellType( XML_TOKEN_INVALID ),
72     mnXfId( -1 ),
73     mbShowPhonetic( false )
74 {
75 }
76 
77 // ----------------------------------------------------------------------------
78 
79 CellFormulaModel::CellFormulaModel() :
80     mnFormulaType( XML_TOKEN_INVALID ),
81     mnSharedId( -1 )
82 {
83 }
84 
85 bool CellFormulaModel::isValidArrayRef( const CellAddress& rCellAddr )
86 {
87     return
88         (maFormulaRef.Sheet == rCellAddr.Sheet) &&
89         (maFormulaRef.StartColumn == rCellAddr.Column) &&
90         (maFormulaRef.StartRow == rCellAddr.Row);
91 }
92 
93 bool CellFormulaModel::isValidSharedRef( const CellAddress& rCellAddr )
94 {
95     return
96         (maFormulaRef.Sheet == rCellAddr.Sheet) &&
97         (maFormulaRef.StartColumn <= rCellAddr.Column) && (rCellAddr.Column <= maFormulaRef.EndColumn) &&
98         (maFormulaRef.StartRow <= rCellAddr.Row) && (rCellAddr.Row <= maFormulaRef.EndRow);
99 }
100 
101 // ----------------------------------------------------------------------------
102 
103 DataTableModel::DataTableModel() :
104     mb2dTable( false ),
105     mbRowTable( false ),
106     mbRef1Deleted( false ),
107     mbRef2Deleted( false )
108 {
109 }
110 
111 // ============================================================================
112 
113 namespace {
114 
115 const sal_Int32 CELLBLOCK_MAXROWS  = 16;    /// Number of rows in a cell block.
116 
117 } // namespace
118 
119 CellBlock::CellBlock( const WorksheetHelper& rHelper, const ValueRange& rColSpan, sal_Int32 nRow ) :
120     WorksheetHelper( rHelper ),
121     maRange( rHelper.getSheetIndex(), rColSpan.mnFirst, nRow, rColSpan.mnLast, nRow ),
122     mnRowLength( rColSpan.mnLast - rColSpan.mnFirst + 1 ),
123     mnFirstFreeIndex( 0 )
124 {
125     maCellArray.realloc( 1 );
126     maCellArray[ 0 ].realloc( mnRowLength );
127     mpCurrCellRow = maCellArray[ 0 ].getArray();
128 }
129 
130 bool CellBlock::isExpandable( const ValueRange& rColSpan ) const
131 {
132     return (maRange.StartColumn == rColSpan.mnFirst) && (maRange.EndColumn == rColSpan.mnLast);
133 }
134 
135 bool CellBlock::isBefore( const ValueRange& rColSpan ) const
136 {
137     return (maRange.EndColumn < rColSpan.mnLast) ||
138         ((maRange.EndColumn == rColSpan.mnLast) && (maRange.StartColumn != rColSpan.mnFirst));
139 }
140 
141 bool CellBlock::contains( sal_Int32 nCol ) const
142 {
143     return (maRange.StartColumn <= nCol) && (nCol <= maRange.EndColumn);
144 }
145 
146 void CellBlock::insertRichString( const CellAddress& rAddress, const RichStringRef& rxString, const Font* pFirstPortionFont )
147 {
148     maRichStrings.push_back( RichStringCell( rAddress, rxString, pFirstPortionFont ) );
149 }
150 
151 void CellBlock::startNextRow()
152 {
153     // fill last cells in current row with empty strings (placeholder for empty cells)
154     fillUnusedCells( mnRowLength );
155     // flush if the cell block reaches maximum size
156     if( maCellArray.getLength() == CELLBLOCK_MAXROWS )
157     {
158         finalizeImport();
159         maRange.StartRow = ++maRange.EndRow;
160         maCellArray.realloc( 1 );
161         mpCurrCellRow = maCellArray[ 0 ].getArray();
162     }
163     else
164     {
165         // prepare next row
166         ++maRange.EndRow;
167         sal_Int32 nRowCount = maCellArray.getLength();
168         maCellArray.realloc( nRowCount + 1 );
169         maCellArray[ nRowCount ].realloc( mnRowLength );
170         mpCurrCellRow = maCellArray[ nRowCount ].getArray();
171     }
172     mnFirstFreeIndex = 0;
173 }
174 
175 Any& CellBlock::getCellAny( sal_Int32 nCol )
176 {
177     OSL_ENSURE( contains( nCol ), "CellBlock::getCellAny - invalid column" );
178     // fill cells before passed column with empty strings (the placeholder for empty cells)
179     sal_Int32 nIndex = nCol - maRange.StartColumn;
180     fillUnusedCells( nIndex );
181     mnFirstFreeIndex = nIndex + 1;
182     return mpCurrCellRow[ nIndex ];
183 }
184 
185 void CellBlock::finalizeImport()
186 {
187     // fill last cells in last row with empty strings (placeholder for empty cells)
188     fillUnusedCells( mnRowLength );
189     // insert all buffered cells into the Calc sheet
190     try
191     {
192         Reference< XCellRangeData > xRangeData( getCellRange( maRange ), UNO_QUERY_THROW );
193         xRangeData->setDataArray( maCellArray );
194     }
195     catch( Exception& )
196     {
197     }
198     // insert uncacheable cells separately
199     for( RichStringCellList::const_iterator aIt = maRichStrings.begin(), aEnd = maRichStrings.end(); aIt != aEnd; ++aIt )
200         putRichString( aIt->maCellAddr, *aIt->mxString, aIt->mpFirstPortionFont );
201 }
202 
203 // private --------------------------------------------------------------------
204 
205 CellBlock::RichStringCell::RichStringCell( const CellAddress& rCellAddr, const RichStringRef& rxString, const Font* pFirstPortionFont ) :
206     maCellAddr( rCellAddr ),
207     mxString( rxString ),
208     mpFirstPortionFont( pFirstPortionFont )
209 {
210 }
211 
212 void CellBlock::fillUnusedCells( sal_Int32 nIndex )
213 {
214     if( mnFirstFreeIndex < nIndex )
215     {
216         Any* pCellEnd = mpCurrCellRow + nIndex;
217         for( Any* pCell = mpCurrCellRow + mnFirstFreeIndex; pCell < pCellEnd; ++pCell )
218             *pCell <<= OUString();
219     }
220 }
221 
222 // ============================================================================
223 
224 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
225     WorksheetHelper( rHelper ),
226     mnCurrRow( -1 )
227 {
228     maCellBlockIt = maCellBlocks.end();
229 }
230 
231 void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
232 {
233     OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
234     OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
235     if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
236         maColSpans[ nRow ] = rColSpans.getRanges();
237 }
238 
239 CellBlock* CellBlockBuffer::getCellBlock( const CellAddress& rCellAddr )
240 {
241     OSL_ENSURE( rCellAddr.Row >= mnCurrRow, "CellBlockBuffer::getCellBlock - passed row out of order" );
242     // prepare cell blocks, if row changes
243     if( rCellAddr.Row != mnCurrRow )
244     {
245         // find colspans for the new row
246         ColSpanVectorMap::iterator aIt = maColSpans.find( rCellAddr.Row );
247 
248         /*  Gap between rows, or rows out of order, or no colspan
249             information for the new row found: flush all open cell blocks. */
250         if( (aIt == maColSpans.end()) || (rCellAddr.Row != mnCurrRow + 1) )
251         {
252             finalizeImport();
253             maCellBlocks.clear();
254             maCellBlockIt = maCellBlocks.end();
255         }
256 
257         /*  Prepare matching cell blocks, create new cell blocks, finalize
258             unmatching cell blocks, if colspan information is available. */
259         if( aIt != maColSpans.end() )
260         {
261             /*  The colspan vector aIt points to is sorted by columns, as well
262                 as the cell block map. In the folloing, this vector and the
263                 list of cell blocks can be iterated simultanously. */
264             CellBlockMap::iterator aMIt = maCellBlocks.begin();
265             const ValueRangeVector& rColRanges = aIt->second;
266             for( ValueRangeVector::const_iterator aVIt = rColRanges.begin(), aVEnd = rColRanges.end(); aVIt != aVEnd; ++aVIt, ++aMIt )
267             {
268                 const ValueRange& rColSpan = *aVIt;
269                 /*  Finalize and remove all cell blocks up to end of the column
270                     range (cell blocks are keyed by end column index).
271                     CellBlock::isBefore() returns true, if the end index of the
272                     passed colspan is greater than the column end index of the
273                     cell block, or if the passed range has the same end index
274                     but the start indexes do not match. */
275                 while( (aMIt != maCellBlocks.end()) && aMIt->second->isBefore( rColSpan ) )
276                 {
277                     aMIt->second->finalizeImport();
278                     maCellBlocks.erase( aMIt++ );
279                 }
280                 /*  If the current cell block (aMIt) fits to the colspan, start
281                     a new row there, otherwise create and insert a new cell block. */
282                 if( (aMIt != maCellBlocks.end()) && aMIt->second->isExpandable( rColSpan ) )
283                     aMIt->second->startNextRow();
284                 else
285                     aMIt = maCellBlocks.insert( aMIt, CellBlockMap::value_type( rColSpan.mnLast,
286                         CellBlockMap::mapped_type( new CellBlock( *this, rColSpan, rCellAddr.Row ) ) ) );
287             }
288             // finalize and remove all remaining cell blocks
289             CellBlockMap::iterator aMEnd = maCellBlocks.end();
290             for( CellBlockMap::iterator aMIt2 = aMIt; aMIt2 != aMEnd; ++aMIt2 )
291                 aMIt2->second->finalizeImport();
292             maCellBlocks.erase( aMIt, aMEnd );
293 
294             // remove cached colspan information (including current one aIt points to)
295             maColSpans.erase( maColSpans.begin(), ++aIt );
296         }
297         maCellBlockIt = maCellBlocks.begin();
298         mnCurrRow = rCellAddr.Row;
299     }
300 
301     // try to find a valid cell block (update maCellBlockIt)
302     if( ((maCellBlockIt != maCellBlocks.end()) && maCellBlockIt->second->contains( rCellAddr.Column )) ||
303         (((maCellBlockIt = maCellBlocks.lower_bound( rCellAddr.Column )) != maCellBlocks.end()) && maCellBlockIt->second->contains( rCellAddr.Column )) )
304     {
305         // maCellBlockIt points to valid cell block
306         return maCellBlockIt->second.get();
307     }
308 
309     // no valid cell block found
310     return 0;
311 }
312 
313 void CellBlockBuffer::finalizeImport()
314 {
315     maCellBlocks.forEachMem( &CellBlock::finalizeImport );
316 }
317 
318 // ============================================================================
319 
320 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
321     WorksheetHelper( rHelper ),
322     maCellBlocks( rHelper ),
323     mbPendingSharedFmla( false )
324 {
325 }
326 
327 void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
328 {
329     maCellBlocks.setColSpans( nRow, rColSpans );
330 }
331 
332 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
333 {
334     setCellFormat( rModel );
335 }
336 
337 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
338 {
339     if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rModel.maCellAddr ) )
340         pCellBlock->getCellAny( rModel.maCellAddr.Column ) <<= fValue;
341     else
342         putValue( rModel.maCellAddr, fValue );
343     setCellFormat( rModel );
344 }
345 
346 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
347 {
348     if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rModel.maCellAddr ) )
349         pCellBlock->getCellAny( rModel.maCellAddr.Column ) <<= rText;
350     else
351         putString( rModel.maCellAddr, rText );
352     setCellFormat( rModel );
353 }
354 
355 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
356 {
357     OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
358     const Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
359     OUString aText;
360     if( rxString->extractPlainString( aText, pFirstPortionFont ) )
361     {
362         setStringCell( rModel, aText );
363     }
364     else
365     {
366         if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rModel.maCellAddr ) )
367             pCellBlock->insertRichString( rModel.maCellAddr, rxString, pFirstPortionFont );
368         else
369             putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
370         setCellFormat( rModel );
371     }
372 }
373 
374 void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
375 {
376     RichStringRef xString = getSharedStrings().getString( nStringId );
377     if( xString.get() )
378         setStringCell( rModel, xString );
379     else
380         setBlankCell( rModel );
381 }
382 
383 void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const DateTime& rDateTime )
384 {
385     // write serial date/time value into the cell
386     double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
387     setValueCell( rModel, fSerial );
388     // set appropriate number format
389     using namespace ::com::sun::star::util::NumberFormat;
390     sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
391     setStandardNumFmt( rModel.maCellAddr, nStdFmt );
392 }
393 
394 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
395 {
396     setCellFormula( rModel.maCellAddr, getFormulaParser().convertBoolToFormula( bValue ) );
397     // #108770# set 'Standard' number format for all Boolean cells
398     setCellFormat( rModel, 0 );
399 }
400 
401 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
402 {
403     setErrorCell( rModel, getUnitConverter().calcBiffErrorCode( rErrorCode ) );
404 }
405 
406 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
407 {
408     setCellFormula( rModel.maCellAddr, getFormulaParser().convertErrorToFormula( nErrorCode ) );
409     setCellFormat( rModel );
410 }
411 
412 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
413 {
414     mbPendingSharedFmla = false;
415     ApiTokenSequence aTokens;
416 
417     /*  Detect special token passed as placeholder for array formulas, shared
418         formulas, and table operations. In BIFF, these formulas are represented
419         by a single tExp resp. tTbl token. If the formula parser finds these
420         tokens, it puts a single OPCODE_BAD token with the base address and
421         formula type into the token sequence. This information will be
422         extracted here, and in case of a shared formula, the shared formula
423         buffer will generate the resulting formula token array. */
424     ApiSpecialTokenInfo aTokenInfo;
425     if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
426     {
427         /*  The second member of the token info is set to true, if the formula
428             represents a table operation, which will be skipped. In BIFF12 it
429             is not possible to distinguish array and shared formulas
430             (BIFF5/BIFF8 provide this information with a special flag in the
431             FORMULA record). */
432         if( !aTokenInfo.Second )
433         {
434             /*  Construct the token array representing the shared formula. If
435                 the returned sequence is empty, the definition of the shared
436                 formula has not been loaded yet, or the cell is part of an
437                 array formula. In this case, the cell will be remembered. After
438                 reading the formula definition it will be retried to insert the
439                 formula via retryPendingSharedFormulaCell(). */
440             BinAddress aBaseAddr( aTokenInfo.First );
441             aTokens = resolveSharedFormula( aBaseAddr );
442             if( !aTokens.hasElements() )
443             {
444                 maSharedFmlaAddr = rModel.maCellAddr;
445                 maSharedBaseAddr = aBaseAddr;
446                 mbPendingSharedFmla = true;
447             }
448         }
449     }
450     else
451     {
452         // simple formula, use the passed token array
453         aTokens = rTokens;
454     }
455 
456     setCellFormula( rModel.maCellAddr, aTokens );
457     setCellFormat( rModel );
458 }
459 
460 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, sal_Int32 nSharedId )
461 {
462     setCellFormula( rModel.maCellAddr, resolveSharedFormula( BinAddress( nSharedId, 0 ) ) );
463     setCellFormat( rModel );
464 }
465 
466 void SheetDataBuffer::createArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens )
467 {
468     /*  Array formulas will be inserted later in finalizeImport(). This is
469         needed to not disturb collecting all the cells, which will be put into
470         the sheet in large blocks to increase performance. */
471     maArrayFormulas.push_back( ArrayFormula( rRange, rTokens ) );
472 }
473 
474 void SheetDataBuffer::createTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel )
475 {
476     /*  Table operations will be inserted later in finalizeImport(). This is
477         needed to not disturb collecting all the cells, which will be put into
478         the sheet in large blocks to increase performance. */
479     maTableOperations.push_back( TableOperation( rRange, rModel ) );
480 }
481 
482 void SheetDataBuffer::createSharedFormula( sal_Int32 nSharedId, const ApiTokenSequence& rTokens )
483 {
484     createSharedFormula( BinAddress( nSharedId, 0 ), rTokens );
485 }
486 
487 void SheetDataBuffer::createSharedFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
488 {
489     createSharedFormula( BinAddress( rCellAddr ), rTokens );
490 }
491 
492 void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
493 {
494     // set row formatting
495     if( bCustomFormat )
496     {
497         // try to expand cached row range, if formatting is equal
498         if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
499         {
500             writeXfIdRowRangeProperties( maXfIdRowRange );
501             maXfIdRowRange.set( nRow, nXfId );
502         }
503     }
504     else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
505     {
506         // finish last cached row range
507         writeXfIdRowRangeProperties( maXfIdRowRange );
508         maXfIdRowRange.set( -1, -1 );
509     }
510 }
511 
512 void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange )
513 {
514     maMergedRanges.push_back( MergedRange( rRange ) );
515 }
516 
517 void SheetDataBuffer::setStandardNumFmt( const CellAddress& rCellAddr, sal_Int16 nStdNumFmt )
518 {
519     try
520     {
521         Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
522         Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
523         sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdNumFmt, Locale() );
524         PropertySet aPropSet( getCell( rCellAddr ) );
525         aPropSet.setProperty( PROP_NumberFormat, nIndex );
526     }
527     catch( Exception& )
528     {
529     }
530 }
531 
532 void SheetDataBuffer::finalizeImport()
533 {
534     // insert all cells of all open cell blocks
535     maCellBlocks.finalizeImport();
536 
537     // create all array formulas
538     for( ArrayFormulaList::iterator aIt = maArrayFormulas.begin(), aEnd = maArrayFormulas.end(); aIt != aEnd; ++aIt )
539         finalizeArrayFormula( aIt->first, aIt->second );
540 
541     // create all table operations
542     for( TableOperationList::iterator aIt = maTableOperations.begin(), aEnd = maTableOperations.end(); aIt != aEnd; ++aIt )
543         finalizeTableOperation( aIt->first, aIt->second );
544 
545     // write default formatting of remaining row range
546     writeXfIdRowRangeProperties( maXfIdRowRange );
547 
548     // try to merge remaining inserted ranges
549     mergeXfIdRanges();
550     // write all formatting
551     for( XfIdRangeMap::const_iterator aIt = maXfIdRanges.begin(), aEnd = maXfIdRanges.end(); aIt != aEnd; ++aIt )
552         writeXfIdRangeProperties( aIt->second );
553 
554     // merge all cached merged ranges and update right/bottom cell borders
555     for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
556         finalizeMergedRange( aIt->maRange );
557     for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
558         finalizeMergedRange( aIt->maRange );
559 }
560 
561 // private --------------------------------------------------------------------
562 
563 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
564     maRowRange( -1 ),
565     mnXfId( -1 )
566 {
567 }
568 
569 bool SheetDataBuffer::XfIdRowRange::intersects( const CellRangeAddress& rRange ) const
570 {
571     return (rRange.StartRow <= maRowRange.mnLast) && (maRowRange.mnFirst <= rRange.EndRow);
572 }
573 
574 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
575 {
576     maRowRange = ValueRange( nRow );
577     mnXfId = nXfId;
578 }
579 
580 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
581 {
582     if( mnXfId == nXfId )
583     {
584         if( maRowRange.mnLast + 1 == nRow )
585         {
586             ++maRowRange.mnLast;
587             return true;
588         }
589         if( maRowRange.mnFirst == nRow + 1 )
590         {
591             --maRowRange.mnFirst;
592             return true;
593         }
594     }
595     return false;
596 }
597 
598 void SheetDataBuffer::XfIdRange::set( const CellAddress& rCellAddr, sal_Int32 nXfId, sal_Int32 nNumFmtId )
599 {
600     maRange.Sheet = rCellAddr.Sheet;
601     maRange.StartColumn = maRange.EndColumn = rCellAddr.Column;
602     maRange.StartRow = maRange.EndRow = rCellAddr.Row;
603     mnXfId = nXfId;
604     mnNumFmtId = nNumFmtId;
605 }
606 
607 bool SheetDataBuffer::XfIdRange::tryExpand( const CellAddress& rCellAddr, sal_Int32 nXfId, sal_Int32 nNumFmtId )
608 {
609     if( (mnXfId == nXfId) && (mnNumFmtId == nNumFmtId) &&
610         (maRange.StartRow == rCellAddr.Row) &&
611         (maRange.EndRow == rCellAddr.Row) &&
612         (maRange.EndColumn + 1 == rCellAddr.Column) )
613     {
614         ++maRange.EndColumn;
615         return true;
616     }
617     return false;
618 }
619 
620 bool SheetDataBuffer::XfIdRange::tryMerge( const XfIdRange& rXfIdRange )
621 {
622     if( (mnXfId == rXfIdRange.mnXfId) &&
623         (mnNumFmtId == rXfIdRange.mnNumFmtId) &&
624         (maRange.EndRow + 1 == rXfIdRange.maRange.StartRow) &&
625         (maRange.StartColumn == rXfIdRange.maRange.StartColumn) &&
626         (maRange.EndColumn == rXfIdRange.maRange.EndColumn) )
627     {
628         maRange.EndRow = rXfIdRange.maRange.EndRow;
629         return true;
630     }
631     return false;
632 }
633 
634 
635 SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
636     maRange( rRange ),
637     mnHorAlign( XML_TOKEN_INVALID )
638 {
639 }
640 
641 SheetDataBuffer::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) :
642     maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ),
643     mnHorAlign( nHorAlign )
644 {
645 }
646 
647 bool SheetDataBuffer::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign )
648 {
649     if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) &&
650         (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) )
651     {
652         ++maRange.EndColumn;
653         return true;
654     }
655     return false;
656 }
657 
658 // ----------------------------------------------------------------------------
659 
660 void SheetDataBuffer::setCellFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
661 {
662     if( rTokens.hasElements() )
663     {
664         if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rCellAddr ) )
665             pCellBlock->getCellAny( rCellAddr.Column ) <<= rTokens;
666         else
667             putFormulaTokens( rCellAddr, rTokens );
668     }
669 }
670 
671 void SheetDataBuffer::createSharedFormula( const BinAddress& rMapKey, const ApiTokenSequence& rTokens )
672 {
673     // create the defined name that will represent the shared formula
674     OUString aName = OUStringBuffer().appendAscii( RTL_CONSTASCII_STRINGPARAM( "__shared_" ) ).
675         append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) ).
676         append( sal_Unicode( '_' ) ).append( rMapKey.mnRow ).
677         append( sal_Unicode( '_' ) ).append( rMapKey.mnCol ).makeStringAndClear();
678     Reference< XNamedRange > xNamedRange = createNamedRangeObject( aName );
679     OSL_ENSURE( xNamedRange.is(), "SheetDataBuffer::createSharedFormula - cannot create shared formula" );
680     PropertySet aNameProps( xNamedRange );
681     aNameProps.setProperty( PROP_IsSharedFormula, true );
682 
683     // get and store the token index of the defined name
684     OSL_ENSURE( maSharedFormulas.count( rMapKey ) == 0, "SheetDataBuffer::createSharedFormula - shared formula exists already" );
685     sal_Int32 nTokenIndex = 0;
686     if( aNameProps.getProperty( nTokenIndex, PROP_TokenIndex ) && (nTokenIndex >= 0) ) try
687     {
688         // store the token index in the map
689         maSharedFormulas[ rMapKey ] = nTokenIndex;
690         // set the formula definition
691         Reference< XFormulaTokens > xTokens( xNamedRange, UNO_QUERY_THROW );
692         xTokens->setTokens( rTokens );
693         // retry to insert a pending shared formula cell
694         if( mbPendingSharedFmla )
695             setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
696     }
697     catch( Exception& )
698     {
699     }
700     mbPendingSharedFmla = false;
701 }
702 
703 ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const BinAddress& rMapKey ) const
704 {
705     sal_Int32 nTokenIndex = ContainerHelper::getMapElement( maSharedFormulas, rMapKey, -1 );
706     return (nTokenIndex >= 0) ? getFormulaParser().convertNameToFormula( nTokenIndex ) : ApiTokenSequence();
707 }
708 
709 void SheetDataBuffer::finalizeArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens ) const
710 {
711     Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
712     OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
713     if( xTokens.is() )
714         xTokens->setArrayTokens( rTokens );
715 }
716 
717 void SheetDataBuffer::finalizeTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel ) const
718 {
719     sal_Int16 nSheet = getSheetIndex();
720     bool bOk = false;
721     if( !rModel.mbRef1Deleted && (rModel.maRef1.getLength() > 0) && (rRange.StartColumn > 0) && (rRange.StartRow > 0) )
722     {
723         CellRangeAddress aOpRange = rRange;
724         CellAddress aRef1;
725         if( getAddressConverter().convertToCellAddress( aRef1, rModel.maRef1, nSheet, true ) ) try
726         {
727             if( rModel.mb2dTable )
728             {
729                 CellAddress aRef2;
730                 if( !rModel.mbRef2Deleted && getAddressConverter().convertToCellAddress( aRef2, rModel.maRef2, nSheet, true ) )
731                 {
732                     // API call expects input values inside operation range
733                     --aOpRange.StartColumn;
734                     --aOpRange.StartRow;
735                     // formula range is top-left cell of operation range
736                     CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn, aOpRange.StartRow, aOpRange.StartColumn, aOpRange.StartRow );
737                     // set multiple operation
738                     Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
739                     xMultOp->setTableOperation( aFormulaRange, TableOperationMode_BOTH, aRef2, aRef1 );
740                     bOk = true;
741                 }
742             }
743             else if( rModel.mbRowTable )
744             {
745                 // formula range is column to the left of operation range
746                 CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn - 1, aOpRange.StartRow, aOpRange.StartColumn - 1, aOpRange.EndRow );
747                 // API call expects input values (top row) inside operation range
748                 --aOpRange.StartRow;
749                 // set multiple operation
750                 Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
751                 xMultOp->setTableOperation( aFormulaRange, TableOperationMode_ROW, aRef1, aRef1 );
752                 bOk = true;
753             }
754             else
755             {
756                 // formula range is row above operation range
757                 CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn, aOpRange.StartRow - 1, aOpRange.EndColumn, aOpRange.StartRow - 1 );
758                 // API call expects input values (left column) inside operation range
759                 --aOpRange.StartColumn;
760                 // set multiple operation
761                 Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
762                 xMultOp->setTableOperation( aFormulaRange, TableOperationMode_COLUMN, aRef1, aRef1 );
763                 bOk = true;
764             }
765         }
766         catch( Exception& )
767         {
768         }
769     }
770 
771     // on error: fill cell range with #REF! error codes
772     if( !bOk ) try
773     {
774         Reference< XCellRangeData > xCellRangeData( getCellRange( rRange ), UNO_QUERY_THROW );
775         size_t nWidth = static_cast< size_t >( rRange.EndColumn - rRange.StartColumn + 1 );
776         size_t nHeight = static_cast< size_t >( rRange.EndRow - rRange.StartRow + 1 );
777         Matrix< Any > aErrorCells( nWidth, nHeight, Any( getFormulaParser().convertErrorToFormula( BIFF_ERR_REF ) ) );
778         xCellRangeData->setDataArray( ContainerHelper::matrixToSequenceSequence( aErrorCells ) );
779     }
780     catch( Exception& )
781     {
782     }
783 }
784 
785 void SheetDataBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
786 {
787     if( (rModel.mnXfId >= 0) || (nNumFmtId >= 0) )
788     {
789         // try to merge existing ranges and to write some formatting properties
790         if( !maXfIdRanges.empty() )
791         {
792             // get row index of last inserted cell
793             sal_Int32 nLastRow = maXfIdRanges.rbegin()->second.maRange.StartRow;
794             // row changed - try to merge ranges of last row with existing ranges
795             if( rModel.maCellAddr.Row != nLastRow )
796             {
797                 mergeXfIdRanges();
798                 // write format properties of all ranges above last row and remove them
799                 XfIdRangeMap::iterator aIt = maXfIdRanges.begin(), aEnd = maXfIdRanges.end();
800                 while( aIt != aEnd )
801                 {
802                     // check that range cannot be merged with current row, and that range is not in cached row range
803                     if( (aIt->second.maRange.EndRow < nLastRow) && !maXfIdRowRange.intersects( aIt->second.maRange ) )
804                     {
805                         writeXfIdRangeProperties( aIt->second );
806                         maXfIdRanges.erase( aIt++ );
807                     }
808                     else
809                         ++aIt;
810                 }
811             }
812         }
813 
814         // try to expand last existing range, or create new range entry
815         if( maXfIdRanges.empty() || !maXfIdRanges.rbegin()->second.tryExpand( rModel.maCellAddr, rModel.mnXfId, nNumFmtId ) )
816             maXfIdRanges[ BinAddress( rModel.maCellAddr ) ].set( rModel.maCellAddr, rModel.mnXfId, nNumFmtId );
817 
818         // update merged ranges for 'center across selection' and 'fill'
819         if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
820         {
821             sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
822             if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
823             {
824                 /*  start new merged range, if cell is not empty (#108781#),
825                     or try to expand last range with empty cell */
826                 if( rModel.mnCellType != XML_TOKEN_INVALID )
827                     maCenterFillRanges.push_back( MergedRange( rModel.maCellAddr, nHorAlign ) );
828                 else if( !maCenterFillRanges.empty() )
829                     maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
830             }
831         }
832     }
833 }
834 
835 void SheetDataBuffer::writeXfIdRowRangeProperties( const XfIdRowRange& rXfIdRowRange ) const
836 {
837     if( (rXfIdRowRange.maRowRange.mnLast >= 0) && (rXfIdRowRange.mnXfId >= 0) )
838     {
839         AddressConverter& rAddrConv = getAddressConverter();
840         CellRangeAddress aRange( getSheetIndex(), 0, rXfIdRowRange.maRowRange.mnFirst, rAddrConv.getMaxApiAddress().Column, rXfIdRowRange.maRowRange.mnLast );
841         if( rAddrConv.validateCellRange( aRange, true, false ) )
842         {
843             PropertySet aPropSet( getCellRange( aRange ) );
844             getStyles().writeCellXfToPropertySet( aPropSet, rXfIdRowRange.mnXfId );
845         }
846     }
847 }
848 
849 void SheetDataBuffer::writeXfIdRangeProperties( const XfIdRange& rXfIdRange ) const
850 {
851     StylesBuffer& rStyles = getStyles();
852     PropertyMap aPropMap;
853     if( rXfIdRange.mnXfId >= 0 )
854         rStyles.writeCellXfToPropertyMap( aPropMap, rXfIdRange.mnXfId );
855     if( rXfIdRange.mnNumFmtId >= 0 )
856         rStyles.writeNumFmtToPropertyMap( aPropMap, rXfIdRange.mnNumFmtId );
857     PropertySet aPropSet( getCellRange( rXfIdRange.maRange ) );
858     aPropSet.setProperties( aPropMap );
859 }
860 
861 void SheetDataBuffer::mergeXfIdRanges()
862 {
863     if( !maXfIdRanges.empty() )
864     {
865         // get row index of last range
866         sal_Int32 nLastRow = maXfIdRanges.rbegin()->second.maRange.StartRow;
867         // process all ranges located in the same row of the last range
868         XfIdRangeMap::iterator aMergeIt = maXfIdRanges.end();
869         while( (aMergeIt != maXfIdRanges.begin()) && ((--aMergeIt)->second.maRange.StartRow == nLastRow) )
870         {
871             const XfIdRange& rMergeXfIdRange = aMergeIt->second;
872             // try to find a range that can be merged with rMergeRange
873             bool bFound = false;
874             for( XfIdRangeMap::iterator aIt = maXfIdRanges.begin(); !bFound && (aIt != aMergeIt); ++aIt )
875                 if( (bFound = aIt->second.tryMerge( rMergeXfIdRange )) == true )
876                     maXfIdRanges.erase( aMergeIt++ );
877         }
878     }
879 }
880 
881 void SheetDataBuffer::finalizeMergedRange( const CellRangeAddress& rRange )
882 {
883     bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
884     bool bMultiRow = rRange.StartRow < rRange.EndRow;
885 
886     if( bMultiCol || bMultiRow ) try
887     {
888         // merge the cell range
889         Reference< XMergeable > xMerge( getCellRange( rRange ), UNO_QUERY_THROW );
890         xMerge->merge( sal_True );
891 
892         // if merging this range worked (no overlapping merged ranges), update cell borders
893         Reference< XCell > xTopLeft( getCell( CellAddress( getSheetIndex(), rRange.StartColumn, rRange.StartRow ) ), UNO_SET_THROW );
894         PropertySet aTopLeftProp( xTopLeft );
895 
896         // copy right border of top-right cell to right border of top-left cell
897         if( bMultiCol )
898         {
899             PropertySet aTopRightProp( getCell( CellAddress( getSheetIndex(), rRange.EndColumn, rRange.StartRow ) ) );
900             BorderLine aLine;
901             if( aTopRightProp.getProperty( aLine, PROP_RightBorder ) )
902                 aTopLeftProp.setProperty( PROP_RightBorder, aLine );
903         }
904 
905         // copy bottom border of bottom-left cell to bottom border of top-left cell
906         if( bMultiRow )
907         {
908             PropertySet aBottomLeftProp( getCell( CellAddress( getSheetIndex(), rRange.StartColumn, rRange.EndRow ) ) );
909             BorderLine aLine;
910             if( aBottomLeftProp.getProperty( aLine, PROP_BottomBorder ) )
911                 aTopLeftProp.setProperty( PROP_BottomBorder, aLine );
912         }
913 
914         // #i93609# merged range in a single row: test if manual row height is needed
915         if( !bMultiRow )
916         {
917             bool bTextWrap = aTopLeftProp.getBoolProperty( PROP_IsTextWrapped );
918             if( !bTextWrap && (xTopLeft->getType() == CellContentType_TEXT) )
919             {
920                 Reference< XText > xText( xTopLeft, UNO_QUERY );
921                 bTextWrap = xText.is() && (xText->getString().indexOf( '\x0A' ) >= 0);
922             }
923             if( bTextWrap )
924                 setManualRowHeight( rRange.StartRow );
925         }
926     }
927     catch( Exception& )
928     {
929     }
930 }
931 
932 // ============================================================================
933 
934 } // namespace xls
935 } // namespace oox
936