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