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