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 67 CellModel::CellModel() : 68 mnCellType( XML_TOKEN_INVALID ), 69 mnXfId( -1 ), 70 mbShowPhonetic( false ) 71 { 72 } 73 74 // ---------------------------------------------------------------------------- 75 76 CellFormulaModel::CellFormulaModel() : 77 mnFormulaType( XML_TOKEN_INVALID ), 78 mnSharedId( -1 ) 79 { 80 } 81 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 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 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 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 127 bool CellBlock::isExpandable( const ValueRange& rColSpan ) const 128 { 129 return (maRange.StartColumn == rColSpan.mnFirst) && (maRange.EndColumn == rColSpan.mnLast); 130 } 131 132 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 138 bool CellBlock::contains( sal_Int32 nCol ) const 139 { 140 return (maRange.StartColumn <= nCol) && (nCol <= maRange.EndColumn); 141 } 142 143 void CellBlock::insertRichString( const CellAddress& rAddress, const RichStringRef& rxString, const Font* pFirstPortionFont ) 144 { 145 maRichStrings.push_back( RichStringCell( rAddress, rxString, pFirstPortionFont ) ); 146 } 147 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 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 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 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 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 221 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) : 222 WorksheetHelper( rHelper ), 223 mnCurrRow( -1 ) 224 { 225 maCellBlockIt = maCellBlocks.end(); 226 } 227 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 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 simultanously. */ 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 310 void CellBlockBuffer::finalizeImport() 311 { 312 maCellBlocks.forEachMem( &CellBlock::finalizeImport ); 313 } 314 315 // ============================================================================ 316 317 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) : 318 WorksheetHelper( rHelper ), 319 maCellBlocks( rHelper ), 320 mbPendingSharedFmla( false ) 321 { 322 } 323 324 void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans ) 325 { 326 maCellBlocks.setColSpans( nRow, rColSpans ); 327 } 328 329 void SheetDataBuffer::setBlankCell( const CellModel& rModel ) 330 { 331 setCellFormat( rModel ); 332 } 333 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 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 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 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 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 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 398 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode ) 399 { 400 setErrorCell( rModel, getUnitConverter().calcBiffErrorCode( rErrorCode ) ); 401 } 402 403 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode ) 404 { 405 setCellFormula( rModel.maCellAddr, getFormulaParser().convertErrorToFormula( nErrorCode ) ); 406 setCellFormat( rModel ); 407 } 408 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 457 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, sal_Int32 nSharedId ) 458 { 459 setCellFormula( rModel.maCellAddr, resolveSharedFormula( BinAddress( nSharedId, 0 ) ) ); 460 setCellFormat( rModel ); 461 } 462 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 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 479 void SheetDataBuffer::createSharedFormula( sal_Int32 nSharedId, const ApiTokenSequence& rTokens ) 480 { 481 createSharedFormula( BinAddress( nSharedId, 0 ), rTokens ); 482 } 483 484 void SheetDataBuffer::createSharedFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens ) 485 { 486 createSharedFormula( BinAddress( rCellAddr ), rTokens ); 487 } 488 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 509 void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange ) 510 { 511 maMergedRanges.push_back( MergedRange( rRange ) ); 512 } 513 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 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 560 SheetDataBuffer::XfIdRowRange::XfIdRowRange() : 561 maRowRange( -1 ), 562 mnXfId( -1 ) 563 { 564 } 565 566 bool SheetDataBuffer::XfIdRowRange::intersects( const CellRangeAddress& rRange ) const 567 { 568 return (rRange.StartRow <= maRowRange.mnLast) && (maRowRange.mnFirst <= rRange.EndRow); 569 } 570 571 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId ) 572 { 573 maRowRange = ValueRange( nRow ); 574 mnXfId = nXfId; 575 } 576 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 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 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 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 632 SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) : 633 maRange( rRange ), 634 mnHorAlign( XML_TOKEN_INVALID ) 635 { 636 } 637 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 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 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 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 700 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 706 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 714 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 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 832 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 846 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 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 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