Bug Summary

File:home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx
Warning:line 672, column 9
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name sheetdatabuffer.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D BOOST_ALL_NO_LIB -D BOOST_ALL_NO_LIB -D BOOST_ALL_NO_LIB -D SYSTEM_ZLIB -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/liborcus/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/liborcus/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/external/clew/source/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sc/source/core/inc -I /home/maarten/src/libreoffice/core/sc/source/filter/inc -I /home/maarten/src/libreoffice/core/sc/source/ui/inc -I /home/maarten/src/libreoffice/core/sc/inc -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/oovbaapi/normal -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/oox/generated -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx
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
22#include <algorithm>
23#include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
24#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
25#include <com/sun/star/table/XCell.hpp>
26#include <com/sun/star/table/XCellRange.hpp>
27#include <com/sun/star/util/DateTime.hpp>
28#include <com/sun/star/util/NumberFormat.hpp>
29#include <com/sun/star/util/XNumberFormatTypes.hpp>
30#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
31#include <sal/log.hxx>
32#include <osl/diagnose.h>
33#include <editeng/boxitem.hxx>
34#include <oox/helper/containerhelper.hxx>
35#include <oox/helper/propertyset.hxx>
36#include <oox/token/properties.hxx>
37#include <oox/token/tokens.hxx>
38#include <addressconverter.hxx>
39#include <formulaparser.hxx>
40#include <sharedstringsbuffer.hxx>
41#include <unitconverter.hxx>
42#include <rangelst.hxx>
43#include <document.hxx>
44#include <scitems.hxx>
45#include <docpool.hxx>
46#include <paramisc.hxx>
47#include <patattr.hxx>
48#include <documentimport.hxx>
49#include <formulabuffer.hxx>
50#include <numformat.hxx>
51#include <sax/tools/converter.hxx>
52
53namespace oox::xls {
54
55using namespace ::com::sun::star::lang;
56using namespace ::com::sun::star::sheet;
57using namespace ::com::sun::star::uno;
58using namespace ::com::sun::star::util;
59
60CellModel::CellModel() :
61 mnCellType( XML_TOKEN_INVALID ),
62 mnXfId( -1 ),
63 mbShowPhonetic( false )
64{
65}
66
67CellFormulaModel::CellFormulaModel() :
68 mnFormulaType( XML_TOKEN_INVALID ),
69 mnSharedId( -1 )
70{
71}
72
73bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
74{
75 return (maFormulaRef.aStart == rCellAddr );
76}
77
78bool 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
86DataTableModel::DataTableModel() :
87 mb2dTable( false ),
88 mbRowTable( false ),
89 mbRef1Deleted( false ),
90 mbRef2Deleted( false )
91{
92}
93
94CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
95 WorksheetHelper( rHelper ),
96 mnCurrRow( -1 )
97{
98}
99
100void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
101{
102 OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" )do { if (true && (!(maColSpans.count( nRow ) == 0))) {
sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"
), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "102" ": "), "%s", "CellBlockBuffer::setColSpans - multiple column spans for the same row"
); } } while (false)
;
103 OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" )do { if (true && (!((mnCurrRow < nRow) && (
maColSpans.empty() || (maColSpans.rbegin()->first < nRow
))))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"
), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "103" ": "), "%s", "CellBlockBuffer::setColSpans - rows are unsorted"
); } } while (false)
;
104 if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
105 {
106 maColSpans[ nRow ] = rColSpans.getRanges();
107 mnCurrRow = nRow;
108 }
109}
110
111SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
112 WorksheetHelper( rHelper ),
113 maCellBlocks( rHelper ),
114 mbPendingSharedFmla( false )
115{
116}
117
118void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
119{
120 maCellBlocks.setColSpans( nRow, rColSpans );
121}
122
123void SheetDataBuffer::setBlankCell( const CellModel& rModel )
124{
125 setCellFormat( rModel );
126}
127
128void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
129{
130 getDocImport().setNumericCell(rModel.maCellAddr, fValue);
131 setCellFormat( rModel );
132}
133
134void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
135{
136 if (!rText.isEmpty())
137 getDocImport().setStringCell(rModel.maCellAddr, rText);
138
139 setCellFormat( rModel );
140}
141
142void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
143{
144 OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" )do { if (true && (!(rxString))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "144" ": "), "%s", "SheetDataBuffer::setStringCell - missing rich string object"
); } } while (false)
;
145 const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
146 OUString aText;
147 if( rxString->extractPlainString( aText, pFirstPortionFont ) )
148 {
149 setStringCell( rModel, aText );
150 }
151 else
152 {
153 putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
154 setCellFormat( rModel );
155 }
156}
157
158void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
159{
160 RichStringRef xString = getSharedStrings().getString( nStringId );
161 if( xString )
162 setStringCell( rModel, xString );
163 else
164 setBlankCell( rModel );
165}
166
167void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
168{
169 // write serial date/time value into the cell
170 double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
171 setValueCell( rModel, fSerial );
172 // set appropriate number format
173 using namespace ::com::sun::star::util::NumberFormat;
174 sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
175 // set number format
176 try
177 {
178 Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
179 Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
180 sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
181 PropertySet aPropSet( getCell( rModel.maCellAddr ) );
182 aPropSet.setProperty( PROP_NumberFormat, nIndex );
183 }
184 catch( Exception& )
185 {
186 }
187}
188
189void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
190{
191 getFormulaBuffer().setCellFormula(
192 rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
193
194 // #108770# set 'Standard' number format for all Boolean cells
195 setCellFormat( rModel );
196}
197
198void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
199{
200 // Using the formula compiler now we can simply pass on the error string.
201 getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
202 setCellFormat( rModel );
203}
204
205void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
206{
207 setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
208}
209
210void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
211{
212 css::util::DateTime aDateTime;
213 if (!sax::Converter::parseDateTime( aDateTime, rDateString))
214 {
215 SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.filter")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case
SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "SheetDataBuffer::setDateCell - could not parse: "
<< rDateString) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "215" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "SheetDataBuffer::setDateCell - could not parse: "
<< rDateString), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "SheetDataBuffer::setDateCell - could not parse: "
<< rDateString; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "215" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "SheetDataBuffer::setDateCell - could not parse: "
<< rDateString) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "215" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "SheetDataBuffer::setDateCell - could not parse: "
<< rDateString), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "SheetDataBuffer::setDateCell - could not parse: "
<< rDateString; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "215" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
216 // At least don't lose data.
217 setStringCell( rModel, rDateString);
218 return;
219 }
220
221 double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
222 setValueCell( rModel, fSerial);
223}
224
225void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
226{
227 BinAddress aAddr(rAddr);
228 maSharedFormulas[aAddr] = rTokens;
229 if( mbPendingSharedFmla )
230 setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
231}
232
233void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
234{
235 mbPendingSharedFmla = false;
236 ApiTokenSequence aTokens;
237
238 /* Detect special token passed as placeholder for array formulas, shared
239 formulas, and table operations. In BIFF, these formulas are represented
240 by a single tExp resp. tTbl token. If the formula parser finds these
241 tokens, it puts a single OPCODE_BAD token with the base address and
242 formula type into the token sequence. This information will be
243 extracted here, and in case of a shared formula, the shared formula
244 buffer will generate the resulting formula token array. */
245 ApiSpecialTokenInfo aTokenInfo;
246 if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
1
Assuming the condition is false
247 {
248 /* The second member of the token info is set to true, if the formula
249 represents a table operation, which will be skipped. In BIFF12 it
250 is not possible to distinguish array and shared formulas
251 (BIFF5/BIFF8 provide this information with a special flag in the
252 FORMULA record). */
253 if( !aTokenInfo.Second )
254 {
255 /* Construct the token array representing the shared formula. If
256 the returned sequence is empty, the definition of the shared
257 formula has not been loaded yet, or the cell is part of an
258 array formula. In this case, the cell will be remembered. After
259 reading the formula definition it will be retried to insert the
260 formula via retryPendingSharedFormulaCell(). */
261 ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
262 aTokens = resolveSharedFormula( aTokenAddr );
263 if( !aTokens.hasElements() )
264 {
265 maSharedFmlaAddr = rModel.maCellAddr;
266 maSharedBaseAddr = aTokenAddr;
267 mbPendingSharedFmla = true;
268 }
269 }
270 }
271 else
272 {
273 // simple formula, use the passed token array
274 aTokens = rTokens;
275 }
276
277 setCellFormula( rModel.maCellAddr, aTokens );
278 setCellFormat( rModel );
2
Calling 'SheetDataBuffer::setCellFormat'
279}
280
281void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
282{
283 /* Array formulas will be inserted later in finalizeImport(). This is
284 needed to not disturb collecting all the cells, which will be put into
285 the sheet in large blocks to increase performance. */
286 maArrayFormulas.emplace_back( rRange, rTokens );
287}
288
289void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
290{
291 /* Table operations will be inserted later in finalizeImport(). This is
292 needed to not disturb collecting all the cells, which will be put into
293 the sheet in large blocks to increase performance. */
294 maTableOperations.emplace_back( rRange, rModel );
295}
296
297void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
298{
299 // set row formatting
300 if( bCustomFormat )
301 {
302 // try to expand cached row range, if formatting is equal
303 if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
304 {
305
306 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
307 maXfIdRowRange.set( nRow, nXfId );
308 }
309 }
310 else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
311 {
312 // finish last cached row range
313 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
314 maXfIdRowRange.set( -1, -1 );
315 }
316}
317
318void SheetDataBuffer::setMergedRange( const ScRange& rRange )
319{
320 maMergedRanges.emplace_back( rRange );
321}
322
323typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
324
325static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
326{
327 Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
328 if ( !pXf1 )
329 return;
330
331 auto it = std::find_if(rMap.begin(), rMap.end(),
332 [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
333 if (rEntry.first.second != nFormatId)
334 return false;
335 Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
336 return *pXf1 == *pXf2;
337 });
338 if (it != rMap.end()) // already exists
339 {
340 // add ranges from the rangelist to the existing rangelist for the
341 // matching style ( should we check if they overlap ? )
342 for (size_t i = 0, nSize = rRangeList.size(); i < nSize; ++i)
343 it->second.push_back(rRangeList[i]);
344 return;
345 }
346 rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
347}
348
349void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ScRange& rAddress, bool bProcessRowRange )
350{
351 RowRangeStyle aStyleRows;
352 aStyleRows.mnNumFmt.first = nXfId;
353 aStyleRows.mnNumFmt.second = nFormatId;
354 aStyleRows.mnStartRow = rAddress.aStart.Row();
355 aStyleRows.mnEndRow = rAddress.aEnd.Row();
356 for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
357 {
358 if ( !bProcessRowRange )
359 maStylesPerColumn[ nCol ].insert( aStyleRows );
360 else
361 {
362 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
363 // Reset row range for each column
364 aStyleRows.mnStartRow = rAddress.aStart.Row();
365 aStyleRows.mnEndRow = rAddress.aEnd.Row();
366
367 // If aStyleRows includes rows already allocated to a style
368 // in rRowStyles, then we need to split it into parts.
369 // ( to occupy only rows that have no style definition)
370
371 // Start iterating at the first element that is not completely before aStyleRows
372 RowStyles::iterator rows_it = rRowStyles.lower_bound(aStyleRows);
373 RowStyles::iterator rows_end = rRowStyles.end();
374 bool bAddRange = true;
375 for ( ; rows_it != rows_end; ++rows_it )
376 {
377 const RowRangeStyle& r = *rows_it;
378
379 // Add the part of aStyleRows that does not overlap with r
380 if ( aStyleRows.mnStartRow < r.mnStartRow )
381 {
382 RowRangeStyle aSplit = aStyleRows;
383 aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, r.mnStartRow - 1);
384 // Insert with hint that aSplit comes directly before the current position
385 rRowStyles.insert( rows_it, aSplit );
386 }
387
388 // Done if no part of aStyleRows extends beyond r
389 if ( aStyleRows.mnEndRow <= r.mnEndRow )
390 {
391 bAddRange = false;
392 break;
393 }
394
395 // Cut off the part aStyleRows that was handled above
396 aStyleRows.mnStartRow = r.mnEndRow + 1;
397 }
398 if ( bAddRange )
399 rRowStyles.insert( aStyleRows );
400 }
401 }
402}
403
404void SheetDataBuffer::finalizeImport()
405{
406 // create all array formulas
407 for( const auto& [rRange, rTokens] : maArrayFormulas )
408 finalizeArrayFormula( rRange, rTokens );
409
410 // create all table operations
411 for( const auto& [rRange, rModel] : maTableOperations )
412 finalizeTableOperation( rRange, rModel );
413
414 // write default formatting of remaining row range
415 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
416
417 std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
418 for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
419 {
420 addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
421 }
422 // gather all ranges that have the same style and apply them in bulk
423 for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
424 {
425 for (size_t i = 0, nSize = rRanges.size(); i < nSize; ++i)
426 addColXfStyle( rFormatKeyPair.first, rFormatKeyPair.second, rRanges[i]);
427 }
428
429 for ( const auto& [rXfId, rRowRangeList] : maXfIdRowRangeList )
430 {
431 if ( rXfId == -1 ) // it's a dud skip it
432 continue;
433 AddressConverter& rAddrConv = getAddressConverter();
434 // get all row ranges for id
435 for ( const auto& rRange : rRowRangeList )
436 {
437 ScRange aRange( 0, rRange.mnFirst, getSheetIndex(),
438 rAddrConv.getMaxApiAddress().Col(), rRange.mnLast, getSheetIndex() );
439
440 addColXfStyle( rXfId, -1, aRange, true );
441 }
442 }
443
444 ScDocumentImport& rDocImport = getDocImport();
445 ScDocument& rDoc = rDocImport.getDoc();
446 StylesBuffer& rStyles = getStyles();
447 for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
448 {
449 SCCOL nScCol = static_cast< SCCOL >( rCol );
450
451 // tdf#91567 Get pattern from the first row without AutoFilter
452 const ScPatternAttr* pDefPattern = nullptr;
453 bool bAutoFilter = true;
454 SCROW nScRow = 0;
455 while ( bAutoFilter && nScRow < rDoc.MaxRow() )
456 {
457 pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
458 if ( pDefPattern )
459 {
460 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
461 bAutoFilter = pAttr->HasAutoFilter();
462 }
463 else
464 break;
465 nScRow++;
466 }
467 if ( !pDefPattern || nScRow == rDoc.MaxRow() )
468 pDefPattern = rDoc.GetDefPattern();
469
470 Xf::AttrList aAttrs(pDefPattern);
471 for ( const auto& rRowStyle : rRowStyles )
472 {
473 Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
474
475 if ( pXf )
476 pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
477 }
478 if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
479 {
480 ScAttrEntry aEntry;
481 aEntry.nEndRow = rDoc.MaxRow();
482 aEntry.pPattern = pDefPattern;
483 rDoc.GetPool()->Put(*aEntry.pPattern);
484 aAttrs.maAttrs.push_back(aEntry);
485
486 if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
487 aAttrs.mbLatinNumFmtOnly = false;
488 }
489
490 ScDocumentImport::Attrs aAttrParam;
491 aAttrParam.mvData.swap(aAttrs.maAttrs);
492 aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
493
494 rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam));
495 }
496
497 // merge all cached merged ranges and update right/bottom cell borders
498 for( const auto& rMergedRange : maMergedRanges )
499 applyCellMerging( rMergedRange.maRange );
500 for( const auto& rCenterFillRange : maCenterFillRanges )
501 applyCellMerging( rCenterFillRange.maRange );
502}
503
504// private --------------------------------------------------------------------
505
506SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
507 maRowRange( -1 ),
508 mnXfId( -1 )
509{
510}
511
512void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
513{
514 maRowRange = ValueRange( nRow );
515 mnXfId = nXfId;
516}
517
518bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
519{
520 if( mnXfId == nXfId )
521 {
522 if( maRowRange.mnLast + 1 == nRow )
523 {
524 ++maRowRange.mnLast;
525 return true;
526 }
527 if( maRowRange.mnFirst == nRow + 1 )
528 {
529 --maRowRange.mnFirst;
530 return true;
531 }
532 }
533 return false;
534}
535
536SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
537 maRange( rRange ),
538 mnHorAlign( XML_TOKEN_INVALID )
539{
540}
541
542SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
543 maRange( rAddress, rAddress ),
544 mnHorAlign( nHorAlign )
545{
546}
547
548bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
549{
550 if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
551 (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
552 {
553 maRange.aEnd.IncCol();
554 return true;
555 }
556 return false;
557}
558
559void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
560{
561 if( rTokens.hasElements() )
562 {
563 putFormulaTokens( rCellAddr, rTokens );
564 }
565}
566
567
568ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
569{
570 BinAddress aAddr(rAddr);
571 ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
572 return aTokens;
573}
574
575void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
576{
577 Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
578 OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" )do { if (true && (!(xTokens.is()))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx"
":" "578" ": "), "%s", "SheetDataBuffer::finalizeArrayFormula - missing formula token interface"
); } } while (false)
;
579 if( xTokens.is() )
580 xTokens->setArrayTokens( rTokens );
581}
582
583void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
584{
585 if (rModel.mbRef1Deleted)
586 return;
587
588 if (rModel.maRef1.isEmpty())
589 return;
590
591 if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
592 return;
593
594 sal_Int16 nSheet = getSheetIndex();
595
596 ScAddress aRef1( 0, 0, 0 );
597 if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
598 return;
599
600 ScDocumentImport& rDoc = getDocImport();
601 ScTabOpParam aParam;
602
603 ScRange aScRange(rRange);
604
605 if (rModel.mb2dTable)
606 {
607 // Two-variable data table.
608 if (rModel.mbRef2Deleted)
609 return;
610
611 if (rModel.maRef2.isEmpty())
612 return;
613
614 ScAddress aRef2( 0, 0, 0 );
615 if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
616 return;
617
618 aParam.meMode = ScTabOpParam::Both;
619
620 aScRange.aStart.IncCol(-1);
621 aScRange.aStart.IncRow(-1);
622
623 aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
624 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
625
626 // Ref1 is row input cell and Ref2 is column input cell.
627 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
628 aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
629 rDoc.setTableOpCells(aScRange, aParam);
630
631 return;
632 }
633
634 // One-variable data table.
635
636 if (rModel.mbRowTable)
637 {
638 // One-variable row input cell (horizontal).
639 aParam.meMode = ScTabOpParam::Row;
640 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
641 aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
642 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
643 aScRange.aStart.IncRow(-1);
644 rDoc.setTableOpCells(aScRange, aParam);
645 }
646 else
647 {
648 // One-variable column input cell (vertical).
649 aParam.meMode = ScTabOpParam::Column;
650 aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
651 aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
652 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
653 aScRange.aStart.IncCol(-1);
654 rDoc.setTableOpCells(aScRange, aParam);
655 }
656}
657
658void SheetDataBuffer::setCellFormat( const CellModel& rModel )
659{
660 if( rModel.mnXfId < 0 )
3
Assuming field 'mnXfId' is >= 0
4
Taking false branch
661 return;
662
663 ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
664 ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
5
Assuming the condition is true
6
'?' condition is true
7
'pLastRange' initialized to a null pointer value
665 /* The xlsx sheet data contains row wise information.
666 * It is sufficient to check if the row range size is one
667 */
668 if (!rRangeList.empty() &&
8
Assuming the condition is false
9
Taking false branch
669 *pLastRange == rModel.maCellAddr)
670 ; // do nothing - this probably bad data
671 else if (!rRangeList.empty() &&
10
Assuming the condition is true
672 pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
11
Called C++ object pointer is null
673 pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
674 pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
675 pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
676 {
677 pLastRange->aEnd.IncCol(); // Expand Column
678 }
679 else
680 {
681 rRangeList.push_back(ScRange(rModel.maCellAddr));
682 pLastRange = &rRangeList.back();
683 }
684
685 if (rRangeList.size() > 1)
686 {
687 for (size_t i = rRangeList.size() - 1; i != 0; --i)
688 {
689 ScRange& rMergeRange = rRangeList[i - 1];
690 if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
691 break;
692
693 /* Try to merge this with the previous range */
694 if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
695 pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
696 pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
697 {
698 rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
699 rRangeList.Remove(rRangeList.size() - 1);
700 break;
701 }
702 else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
703 break; // Un-necessary to check with any other rows
704 }
705 }
706 // update merged ranges for 'center across selection' and 'fill'
707 const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
708 if( !pXf )
709 return;
710
711 sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
712 if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
713 {
714 /* start new merged range, if cell is not empty (#108781#),
715 or try to expand last range with empty cell */
716 if( rModel.mnCellType != XML_TOKEN_INVALID )
717 maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
718 else if( !maCenterFillRanges.empty() )
719 maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
720 }
721}
722
723static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
724{
725 SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
726 SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
727
728 const SvxBoxItem* pFromItem =
729 rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
730 const SvxBoxItem* pToItem =
731 rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
732
733 SvxBoxItem aNewItem( *pToItem );
734 aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
735 rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
736}
737
738void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
739{
740 bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
741 bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
742
743 const ScAddress& rStart = rRange.aStart;
744 const ScAddress& rEnd = rRange.aEnd;
745 ScDocument& rDoc = getScDocument();
746 // set correct right border
747 if( bMultiCol )
748 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
749 // set correct lower border
750 if( bMultiRow )
751 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
752 // do merge
753 if( bMultiCol || bMultiRow )
754 rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
755}
756
757} // namespace oox
758
759/* vim:set shiftwidth=4 softtabstop=4 expandtab: */