xref: /core/sc/source/filter/oox/sheetdatabuffer.cxx (revision 000aaf1f)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sheetdatabuffer.hxx>
21 #include <patterncache.hxx>
22 
23 #include <algorithm>
24 #include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
25 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
26 #include <com/sun/star/util/DateTime.hpp>
27 #include <com/sun/star/util/NumberFormat.hpp>
28 #include <com/sun/star/util/XNumberFormatTypes.hpp>
29 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
30 #include <sal/log.hxx>
31 #include <osl/diagnose.h>
32 #include <editeng/boxitem.hxx>
33 #include <oox/helper/containerhelper.hxx>
34 #include <oox/helper/propertyset.hxx>
35 #include <oox/token/properties.hxx>
36 #include <oox/token/tokens.hxx>
37 #include <addressconverter.hxx>
38 #include <formulaparser.hxx>
39 #include <sharedstringsbuffer.hxx>
40 #include <unitconverter.hxx>
41 #include <rangelst.hxx>
42 #include <document.hxx>
43 #include <scitems.hxx>
44 #include <docpool.hxx>
45 #include <paramisc.hxx>
46 #include <patattr.hxx>
47 #include <documentimport.hxx>
48 #include <formulabuffer.hxx>
49 #include <numformat.hxx>
50 #include <sax/tools/converter.hxx>
51 #include <docuno.hxx>
52 
53 namespace oox::xls {
54 
55 using namespace ::com::sun::star::lang;
56 using namespace ::com::sun::star::sheet;
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::util;
59 
CellModel()60 CellModel::CellModel() :
61     mnCellType( XML_TOKEN_INVALID ),
62     mnXfId( -1 ),
63     mbShowPhonetic( false )
64 {
65 }
66 
CellFormulaModel()67 CellFormulaModel::CellFormulaModel() :
68     mnFormulaType( XML_TOKEN_INVALID ),
69     mnSharedId( -1 )
70 {
71 }
72 
isValidArrayRef(const ScAddress & rCellAddr)73 bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
74 {
75     return (maFormulaRef.aStart == rCellAddr );
76 }
77 
isValidSharedRef(const ScAddress & rCellAddr)78 bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr )
79 {
80     return
81         (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) &&
82         (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) &&
83         (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row());
84 }
85 
DataTableModel()86 DataTableModel::DataTableModel() :
87     mb2dTable( false ),
88     mbRowTable( false ),
89     mbRef1Deleted( false ),
90     mbRef2Deleted( false )
91 {
92 }
93 
SheetDataBuffer(const WorksheetHelper & rHelper)94 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
95     WorksheetHelper( rHelper ),
96     mbPendingSharedFmla( false )
97 {
98 }
99 
setBlankCell(const CellModel & rModel)100 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
101 {
102     setCellFormat( rModel );
103 }
104 
setValueCell(const CellModel & rModel,double fValue)105 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
106 {
107     getDocImport().setNumericCell(rModel.maCellAddr, fValue);
108     setCellFormat( rModel );
109 }
110 
setStringCell(const CellModel & rModel,const OUString & rText)111 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
112 {
113     if (!rText.isEmpty())
114         getDocImport().setStringCell(rModel.maCellAddr, rText);
115 
116     setCellFormat( rModel );
117 }
118 
setStringCell(const CellModel & rModel,const RichStringRef & rxString)119 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
120 {
121     OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" );
122     const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
123     const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
124     bool bSingleLine = pXf ? !pXf->getAlignment().getModel().mbWrapText : false;
125     OUString aText;
126     if( rxString->extractPlainString( aText, pFirstPortionFont ) )
127     {
128         setStringCell( rModel, aText );
129     }
130     else
131     {
132         putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont, bSingleLine );
133         setCellFormat( rModel );
134     }
135 }
136 
setStringCell(const CellModel & rModel,sal_Int32 nStringId)137 void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
138 {
139     RichStringRef xString = getSharedStrings().getString( nStringId );
140     if( xString )
141         setStringCell( rModel, xString );
142     else
143         setBlankCell( rModel );
144 }
145 
setDateTimeCell(const CellModel & rModel,const css::util::DateTime & rDateTime)146 void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
147 {
148     // write serial date/time value into the cell
149     double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
150     setValueCell( rModel, fSerial );
151     // set appropriate number format
152     using namespace ::com::sun::star::util::NumberFormat;
153     sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
154     // set number format
155     try
156     {
157         Reference< XNumberFormatsSupplier > xNumFmtsSupp( static_cast<cppu::OWeakObject*>(getDocument().get()), UNO_QUERY_THROW );
158         Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
159         sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
160         PropertySet aPropSet( getCell( rModel.maCellAddr ) );
161         aPropSet.setProperty( PROP_NumberFormat, nIndex );
162     }
163     catch( Exception& )
164     {
165     }
166 }
167 
setBooleanCell(const CellModel & rModel,bool bValue)168 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
169 {
170     getFormulaBuffer().setCellFormula(
171         rModel.maCellAddr, bValue ? u"TRUE()"_ustr : u"FALSE()"_ustr);
172 
173     // #108770# set 'Standard' number format for all Boolean cells
174     setCellFormat( rModel );
175 }
176 
setErrorCell(const CellModel & rModel,const OUString & rErrorCode)177 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
178 {
179     // Using the formula compiler now we can simply pass on the error string.
180     getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
181     setCellFormat( rModel );
182 }
183 
setErrorCell(const CellModel & rModel,sal_uInt8 nErrorCode)184 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
185 {
186     setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
187 }
188 
setDateCell(const CellModel & rModel,const OUString & rDateString)189 void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
190 {
191     css::util::DateTime aDateTime;
192     if (!sax::Converter::parseDateTime( aDateTime, rDateString))
193     {
194         SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
195         // At least don't lose data.
196         setStringCell( rModel, rDateString);
197         return;
198     }
199 
200     double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
201     setValueCell( rModel, fSerial);
202 }
203 
createSharedFormula(const ScAddress & rAddr,const ApiTokenSequence & rTokens)204 void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
205 {
206     BinAddress aAddr(rAddr);
207     maSharedFormulas[aAddr] = rTokens;
208     if( mbPendingSharedFmla )
209         setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
210 }
211 
setFormulaCell(const CellModel & rModel,const ApiTokenSequence & rTokens)212 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
213 {
214     mbPendingSharedFmla = false;
215     ApiTokenSequence aTokens;
216 
217     /*  Detect special token passed as placeholder for array formulas, shared
218         formulas, and table operations. In BIFF, these formulas are represented
219         by a single tExp resp. tTbl token. If the formula parser finds these
220         tokens, it puts a single OPCODE_BAD token with the base address and
221         formula type into the token sequence. This information will be
222         extracted here, and in case of a shared formula, the shared formula
223         buffer will generate the resulting formula token array. */
224     ApiSpecialTokenInfo aTokenInfo;
225     if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
226     {
227         /*  The second member of the token info is set to true, if the formula
228             represents a table operation, which will be skipped. In BIFF12 it
229             is not possible to distinguish array and shared formulas
230             (BIFF5/BIFF8 provide this information with a special flag in the
231             FORMULA record). */
232         if( !aTokenInfo.Second )
233         {
234             /*  Construct the token array representing the shared formula. If
235                 the returned sequence is empty, the definition of the shared
236                 formula has not been loaded yet, or the cell is part of an
237                 array formula. In this case, the cell will be remembered. After
238                 reading the formula definition it will be retried to insert the
239                 formula via retryPendingSharedFormulaCell(). */
240             ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
241             aTokens = resolveSharedFormula( aTokenAddr );
242             if( !aTokens.hasElements() )
243             {
244                 maSharedFmlaAddr = rModel.maCellAddr;
245                 maSharedBaseAddr = aTokenAddr;
246                 mbPendingSharedFmla = true;
247             }
248         }
249     }
250     else
251     {
252         // simple formula, use the passed token array
253         aTokens = rTokens;
254     }
255 
256     setCellFormula( rModel.maCellAddr, aTokens );
257     setCellFormat( rModel );
258 }
259 
createArrayFormula(const ScRange & rRange,const ApiTokenSequence & rTokens)260 void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
261 {
262     /*  Array formulas will be inserted later in finalizeImport(). This is
263         needed to not disturb collecting all the cells, which will be put into
264         the sheet in large blocks to increase performance. */
265     maArrayFormulas.emplace_back( rRange, rTokens );
266 }
267 
createTableOperation(const ScRange & rRange,const DataTableModel & rModel)268 void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
269 {
270     /*  Table operations will be inserted later in finalizeImport(). This is
271         needed to not disturb collecting all the cells, which will be put into
272         the sheet in large blocks to increase performance. */
273     maTableOperations.emplace_back( rRange, rModel );
274 }
275 
setRowFormat(sal_Int32 nRow,sal_Int32 nXfId,bool bCustomFormat)276 void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
277 {
278     // set row formatting
279     if( bCustomFormat )
280     {
281         // try to expand cached row range, if formatting is equal
282         if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
283         {
284 
285             maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
286             maXfIdRowRange.set( nRow, nXfId );
287         }
288     }
289     else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
290     {
291         // finish last cached row range
292         maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
293         maXfIdRowRange.set( -1, -1 );
294     }
295 }
296 
setMergedRange(const ScRange & rRange)297 void SheetDataBuffer::setMergedRange( const ScRange& rRange )
298 {
299     maMergedRanges.emplace_back( rRange );
300 }
301 
302 typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
303 
addIfNotInMyMap(const StylesBuffer & rStyles,std::map<FormatKeyPair,ScRangeList> & rMap,sal_Int32 nXfId,sal_Int32 nFormatId,const ScRangeList & rRangeList)304 static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
305 {
306     Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
307     if ( !pXf1 )
308         return;
309 
310     auto it = std::find_if(rMap.begin(), rMap.end(),
311         [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
312             if (rEntry.first.second != nFormatId)
313                 return false;
314             Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
315             return *pXf1 == *pXf2;
316         });
317     if (it != rMap.end()) // already exists
318     {
319         // add ranges from the rangelist to the existing rangelist for the
320         // matching style ( should we check if they overlap ? )
321         it->second.insert(it->second.end(), rRangeList.begin(), rRangeList.end());
322         return;
323     }
324     rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
325 }
326 
addColXfStyles()327 void SheetDataBuffer::addColXfStyles()
328 {
329     std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
330     for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
331     {
332         addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
333     }
334     // gather all ranges that have the same style and apply them in bulk
335     // Collect data in unsorted vectors and sort them just once at the end
336     // instead of possibly slow repeated inserts.
337     TmpColStyles tmpStylesPerColumn;
338     for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
339     {
340         for (const ScRange & rAddress : rRanges)
341         {
342             RowRangeStyle aStyleRows;
343             aStyleRows.mnNumFmt.first = rFormatKeyPair.first;
344             aStyleRows.mnNumFmt.second = rFormatKeyPair.second;
345             aStyleRows.mnStartRow = rAddress.aStart.Row();
346             aStyleRows.mnEndRow = rAddress.aEnd.Row();
347             for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
348                tmpStylesPerColumn[ nCol ].push_back( aStyleRows );
349         }
350     }
351     for( auto& rowStyles : tmpStylesPerColumn )
352     {
353         TmpRowStyles& s = rowStyles.second;
354         std::sort( s.begin(), s.end(), StyleRowRangeComp());
355         s.erase( std::unique( s.begin(), s.end(),
356                     [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
357                         // Synthetize operator== from operator < . Do not create an actual operator==
358                         // as operator< is somewhat specific (see StyleRowRangeComp).
359                         { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
360             s.end());
361         // Broken documents may have overlapping ranges that cause problems, repeat once more.
362         if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
363         {
364             std::sort( s.begin(), s.end(), StyleRowRangeComp());
365             s.erase( std::unique( s.begin(), s.end(),
366                         [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
367                             { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
368                 s.end());
369         }
370         maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( std::move( s ));
371     }
372 }
373 
addColXfStyleProcessRowRanges()374 void SheetDataBuffer::addColXfStyleProcessRowRanges()
375 {
376     // count the number of row-range-styles we have
377     AddressConverter& rAddrConv = getAddressConverter();
378     int cnt = 0;
379     for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
380     {
381         if ( nXfId == -1 ) // it's a dud skip it
382             continue;
383         cnt += rRowRangeList.size();
384     }
385     // pre-allocate space in the sorted_vector
386     for ( sal_Int32 nCol = 0; nCol <= rAddrConv.getMaxApiAddress().Col(); ++nCol )
387     {
388        RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
389        rRowStyles.reserve(rRowStyles.size() + cnt);
390     }
391     const auto nMaxCol = rAddrConv.getMaxApiAddress().Col();
392     for ( sal_Int32 nCol = 0; nCol <= nMaxCol; ++nCol )
393     {
394         RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
395         for ( auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
396         {
397             if ( nXfId == -1 ) // it's a dud skip it
398                 continue;
399             // sort the row ranges, so we spend less time moving data around
400             // when we insert into aStyleRows
401             std::sort(rRowRangeList.begin(), rRowRangeList.end(),
402                 [](const ValueRange& lhs, const ValueRange& rhs)
403                 {
404                     return lhs.mnFirst < rhs.mnFirst;
405                 });
406             // get all row ranges for id
407             for ( const auto& rRange : rRowRangeList )
408             {
409                 RowRangeStyle aStyleRows;
410                 aStyleRows.mnNumFmt.first = nXfId;
411                 aStyleRows.mnNumFmt.second = -1;
412 
413                 // Reset row range for each column
414                 aStyleRows.mnStartRow = rRange.mnFirst;
415                 aStyleRows.mnEndRow = rRange.mnLast;
416 
417                 // If aStyleRows includes rows already allocated to a style
418                 // in rRowStyles, then we need to split it into parts.
419                 // ( to occupy only rows that have no style definition)
420 
421                 // Start iterating at the first element that is not completely before aStyleRows
422                 RowStyles::const_iterator rows_it = rRowStyles.lower_bound(aStyleRows);
423                 bool bAddRange = true;
424                 for ( ; rows_it != rRowStyles.end(); ++rows_it )
425                 {
426                     // Add the part of aStyleRows that does not overlap with r
427                     if ( aStyleRows.mnStartRow < rows_it->mnStartRow )
428                     {
429                         RowRangeStyle aSplit = aStyleRows;
430                         aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, rows_it->mnStartRow - 1);
431                         rows_it = rRowStyles.insert( aSplit ).first;
432                     }
433 
434                     // Done if no part of aStyleRows extends beyond r
435                     if ( aStyleRows.mnEndRow <= rows_it->mnEndRow )
436                     {
437                         bAddRange = false;
438                         break;
439                     }
440 
441                     // Cut off the part aStyleRows that was handled above
442                     aStyleRows.mnStartRow = rows_it->mnEndRow + 1;
443                 }
444                 if ( bAddRange )
445                     rRowStyles.insert( aStyleRows );
446             }
447         }
448     }
449 }
450 
finalizeImport()451 void SheetDataBuffer::finalizeImport()
452 {
453     ScDocumentImport& rDocImport = getDocImport();
454 
455     SCTAB nStartTabInvalidatedIters(SCTAB_MAX);
456     SCTAB nEndTabInvalidatedIters(0);
457 
458     // create all array formulas
459     for( const auto& [rRange, rTokens] : maArrayFormulas )
460     {
461         finalizeArrayFormula(rRange, rTokens);
462 
463         nStartTabInvalidatedIters = std::min(rRange.aStart.Tab(), nStartTabInvalidatedIters);
464         nEndTabInvalidatedIters = std::max(rRange.aEnd.Tab(), nEndTabInvalidatedIters);
465     }
466 
467     for (SCTAB nTab = nStartTabInvalidatedIters; nTab <= nEndTabInvalidatedIters; ++nTab)
468         rDocImport.invalidateBlockPositionSet(nTab);
469 
470     // create all table operations
471     for( const auto& [rRange, rModel] : maTableOperations )
472         finalizeTableOperation( rRange, rModel );
473 
474     // write default formatting of remaining row range
475     maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
476 
477     addColXfStyles();
478 
479     addColXfStyleProcessRowRanges();
480 
481     ScDocument& rDoc = rDocImport.getDoc();
482     StylesBuffer& rStyles = getStyles();
483     ScDocumentImport::Attrs aPendingAttrParam;
484     SCCOL pendingColStart = -1;
485     SCCOL pendingColEnd = -1;
486     ScPatternCache aPatternCache;
487 
488     for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
489     {
490         SCCOL nScCol = static_cast< SCCOL >( rCol );
491 
492         // tdf#91567 Get pattern from the first row without AutoFilter
493         const ScPatternAttr* pDefPattern = nullptr;
494         bool bAutoFilter = true;
495         SCROW nScRow = 0;
496         while ( bAutoFilter && nScRow < rDoc.MaxRow() )
497         {
498             pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
499             if ( pDefPattern )
500             {
501                 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
502                 bAutoFilter = pAttr->HasAutoFilter();
503             }
504             else
505                 break;
506             nScRow++;
507         }
508         if ( !pDefPattern || nScRow == rDoc.MaxRow() )
509             pDefPattern = &rDoc.getCellAttributeHelper().getDefaultCellAttribute();
510 
511         Xf::AttrList aAttrs(pDefPattern);
512         for ( const auto& rRowStyle : rRowStyles )
513         {
514              Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
515 
516              if ( pXf )
517                  pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow,
518                     rRowStyle.mnNumFmt.first, rRowStyle.mnNumFmt.second, aPatternCache );
519         }
520         if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
521         {
522             ScAttrEntry aEntry;
523             aEntry.nEndRow = rDoc.MaxRow();
524             aEntry.setScPatternAttr(pDefPattern, false);
525             aAttrs.maAttrs.push_back(aEntry);
526 
527             if (!sc::NumFmtUtil::isLatinScript(*aEntry.getScPatternAttr(), rDoc))
528                 aAttrs.mbLatinNumFmtOnly = false;
529         }
530 
531         ScDocumentImport::Attrs aAttrParam;
532         aAttrParam.mvData.swap(aAttrs.maAttrs);
533         aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
534 
535         // Compress setting the attributes, set the same set in one call.
536         if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
537             ++pendingColEnd;
538         else
539         {
540             if( pendingColStart != -1 )
541                 rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
542             pendingColStart = pendingColEnd = nScCol;
543             aPendingAttrParam = std::move( aAttrParam );
544         }
545     }
546     if( pendingColStart != -1 )
547         rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
548 
549     // merge all cached merged ranges and update right/bottom cell borders
550     for( const auto& rMergedRange : maMergedRanges )
551         applyCellMerging( rMergedRange.maRange );
552     for( const auto& rCenterFillRange : maCenterFillRanges )
553         applyCellMerging( rCenterFillRange.maRange );
554 }
555 
556 // private --------------------------------------------------------------------
557 
XfIdRowRange()558 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
559     maRowRange( -1 ),
560     mnXfId( -1 )
561 {
562 }
563 
set(sal_Int32 nRow,sal_Int32 nXfId)564 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
565 {
566     maRowRange = ValueRange( nRow );
567     mnXfId = nXfId;
568 }
569 
tryExpand(sal_Int32 nRow,sal_Int32 nXfId)570 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
571 {
572     if( mnXfId == nXfId )
573     {
574         if( maRowRange.mnLast + 1 == nRow )
575         {
576             ++maRowRange.mnLast;
577             return true;
578         }
579         if( maRowRange.mnFirst == nRow + 1 )
580         {
581             --maRowRange.mnFirst;
582             return true;
583         }
584     }
585     return false;
586 }
587 
MergedRange(const ScRange & rRange)588 SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
589     maRange( rRange ),
590     mnHorAlign( XML_TOKEN_INVALID )
591 {
592 }
593 
MergedRange(const ScAddress & rAddress,sal_Int32 nHorAlign)594 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
595     maRange( rAddress, rAddress ),
596     mnHorAlign( nHorAlign )
597 {
598 }
599 
tryExpand(const ScAddress & rAddress,sal_Int32 nHorAlign)600 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
601 {
602     if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
603         (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
604     {
605         maRange.aEnd.IncCol();
606         return true;
607     }
608     return false;
609 }
610 
setCellFormula(const ScAddress & rCellAddr,const ApiTokenSequence & rTokens)611 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
612 {
613     if( rTokens.hasElements() )
614     {
615         putFormulaTokens( rCellAddr, rTokens );
616     }
617 }
618 
619 
resolveSharedFormula(const ScAddress & rAddr) const620 ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
621 {
622     BinAddress aAddr(rAddr);
623     ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
624     return aTokens;
625 }
626 
finalizeArrayFormula(const ScRange & rRange,const ApiTokenSequence & rTokens) const627 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
628 {
629     Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
630     OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
631     if( xTokens.is() )
632         xTokens->setArrayTokens( rTokens );
633 }
634 
finalizeTableOperation(const ScRange & rRange,const DataTableModel & rModel)635 void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
636 {
637     if (rModel.mbRef1Deleted)
638         return;
639 
640     if (rModel.maRef1.isEmpty())
641         return;
642 
643     if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
644         return;
645 
646     sal_Int16 nSheet = getSheetIndex();
647 
648     ScAddress aRef1( 0, 0, 0 );
649     if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
650         return;
651 
652     ScDocumentImport& rDoc = getDocImport();
653     ScTabOpParam aParam;
654 
655     ScRange aScRange(rRange);
656 
657     if (rModel.mb2dTable)
658     {
659         // Two-variable data table.
660         if (rModel.mbRef2Deleted)
661             return;
662 
663         if (rModel.maRef2.isEmpty())
664             return;
665 
666         ScAddress aRef2( 0, 0, 0 );
667         if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
668             return;
669 
670         aParam.meMode = ScTabOpParam::Both;
671 
672         aScRange.aStart.IncCol(-1);
673         aScRange.aStart.IncRow(-1);
674 
675         aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
676         aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
677 
678         // Ref1 is row input cell and Ref2 is column input cell.
679         aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
680         aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
681         rDoc.setTableOpCells(aScRange, aParam);
682 
683         return;
684     }
685 
686     // One-variable data table.
687 
688     if (rModel.mbRowTable)
689     {
690         // One-variable row input cell (horizontal).
691         aParam.meMode = ScTabOpParam::Row;
692         aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
693         aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
694         aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
695         aScRange.aStart.IncRow(-1);
696         rDoc.setTableOpCells(aScRange, aParam);
697     }
698     else
699     {
700         // One-variable column input cell (vertical).
701         aParam.meMode = ScTabOpParam::Column;
702         aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
703         aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
704         aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
705         aScRange.aStart.IncCol(-1);
706         rDoc.setTableOpCells(aScRange, aParam);
707     }
708 }
709 
setCellFormat(const CellModel & rModel)710 void SheetDataBuffer::setCellFormat( const CellModel& rModel )
711 {
712     if( rModel.mnXfId < 0 )
713         return;
714 
715     ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
716     ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
717     /* The xlsx sheet data contains row wise information.
718      * It is sufficient to check if the row range size is one
719      */
720     if (!rRangeList.empty() &&
721         *pLastRange == rModel.maCellAddr)
722         ; // do nothing - this probably bad data
723     else if (!rRangeList.empty() &&
724         pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
725         pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
726         pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
727         pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
728     {
729         pLastRange->aEnd.IncCol();       // Expand Column
730     }
731     else
732     {
733         rRangeList.push_back(ScRange(rModel.maCellAddr));
734         pLastRange = &rRangeList.back();
735     }
736 
737     if (rRangeList.size() > 1)
738     {
739         for (size_t i = rRangeList.size() - 1; i != 0; --i)
740         {
741             ScRange& rMergeRange = rRangeList[i - 1];
742             if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
743                 break;
744 
745             /* Try to merge this with the previous range */
746             if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
747                 pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
748                 pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
749             {
750                 rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
751                 rRangeList.Remove(rRangeList.size() - 1);
752                 break;
753             }
754             else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
755                 break; // Un-necessary to check with any other rows
756         }
757     }
758     // update merged ranges for 'center across selection' and 'fill'
759     const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
760     if( !pXf )
761         return;
762 
763     sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
764     if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
765     {
766         /*  start new merged range, if cell is not empty (#108781#),
767             or try to expand last range with empty cell */
768         if( rModel.mnCellType != XML_TOKEN_INVALID )
769             maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
770         else if( !maCenterFillRanges.empty() )
771             maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
772     }
773 }
774 
lcl_SetBorderLine(ScDocument & rDoc,const ScRange & rRange,SCTAB nScTab,SvxBoxItemLine nLine)775 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
776 {
777     SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
778     SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
779 
780     const SvxBoxItem* pFromItem =
781         rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
782     const SvxBoxItem* pToItem =
783         rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
784 
785     SvxBoxItem aNewItem( *pToItem );
786     aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
787     rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
788 }
789 
applyCellMerging(const ScRange & rRange)790 void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
791 {
792     bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
793     bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
794 
795     const ScAddress& rStart = rRange.aStart;
796     const ScAddress& rEnd = rRange.aEnd;
797     ScDocument& rDoc = getScDocument();
798     // set correct right border
799     if( bMultiCol )
800         lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
801         // set correct lower border
802     if( bMultiRow )
803         lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
804     // do merge
805     if( bMultiCol || bMultiRow )
806         rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), getSheetIndex() );
807 }
808 
809 } // namespace oox
810 
811 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
812