LibreOffice Module sc (master) 1
rangeseq.cxx
Go to the documentation of this file.
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 <svl/numformat.hxx>
21#include <rtl/math.hxx>
23#include <osl/diagnose.h>
24#include <osl/thread.h>
25
26#include <com/sun/star/uno/Any.hxx>
27#include <com/sun/star/uno/Sequence.hxx>
28#include <comphelper/string.hxx>
29#include <rangeseq.hxx>
30#include <document.hxx>
31#include <dociter.hxx>
32#include <scmatrix.hxx>
33#include <formulacell.hxx>
34
35using namespace com::sun::star;
36
37static bool lcl_HasErrors( ScDocument& rDoc, const ScRange& rRange )
38{
39 // no need to look at empty cells - just use ScCellIterator
40 ScCellIterator aIter( rDoc, rRange );
41 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
42 {
43 if (aIter.getType() != CELLTYPE_FORMULA)
44 continue;
45
46 ScFormulaCell* pCell = aIter.getFormulaCell();
47 if (pCell->GetErrCode() != FormulaError::NONE)
48 return true;
49 }
50 return false; // no error found
51}
52
53static tools::Long lcl_DoubleToLong( double fVal )
54{
55 double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) :
56 ::rtl::math::approxCeil( fVal );
57 if ( o3tl::convertsToAtLeast(fInt, LONG_MIN) && o3tl::convertsToAtMost(fInt, LONG_MAX) )
58 return static_cast<tools::Long>(fInt);
59 else
60 return 0; // out of range
61}
62
63bool ScRangeToSequence::FillLongArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange )
64{
65 SCTAB nTab = rRange.aStart.Tab();
66 SCCOL nStartCol = rRange.aStart.Col();
67 SCROW nStartRow = rRange.aStart.Row();
68 sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
69 sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
70
71 uno::Sequence< uno::Sequence<sal_Int32> > aRowSeq( nRowCount );
72 uno::Sequence<sal_Int32>* pRowAry = aRowSeq.getArray();
73 for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
74 {
75 uno::Sequence<sal_Int32> aColSeq( nColCount );
76 sal_Int32* pColAry = aColSeq.getArray();
77 for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
78 pColAry[nCol] = lcl_DoubleToLong( rDoc.GetValue(
79 ScAddress( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab ) ) );
80
81 pRowAry[nRow] = aColSeq;
82 }
83
84 rAny <<= aRowSeq;
85 return !lcl_HasErrors( rDoc, rRange );
86}
87
88bool ScRangeToSequence::FillLongArray( uno::Any& rAny, const ScMatrix* pMatrix )
89{
90 if (!pMatrix)
91 return false;
92
93 SCSIZE nColCount;
94 SCSIZE nRowCount;
95 pMatrix->GetDimensions( nColCount, nRowCount );
96
97 uno::Sequence< uno::Sequence<sal_Int32> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
98 uno::Sequence<sal_Int32>* pRowAry = aRowSeq.getArray();
99 for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
100 {
101 uno::Sequence<sal_Int32> aColSeq( static_cast<sal_Int32>(nColCount) );
102 sal_Int32* pColAry = aColSeq.getArray();
103 for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
104 if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
105 pColAry[nCol] = 0;
106 else
107 pColAry[nCol] = lcl_DoubleToLong( pMatrix->GetDouble( nCol, nRow ) );
108
109 pRowAry[nRow] = aColSeq;
110 }
111
112 rAny <<= aRowSeq;
113 return true;
114}
115
116bool ScRangeToSequence::FillDoubleArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange )
117{
118 SCTAB nTab = rRange.aStart.Tab();
119 SCCOL nStartCol = rRange.aStart.Col();
120 SCROW nStartRow = rRange.aStart.Row();
121 sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
122 sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
123
124 uno::Sequence< uno::Sequence<double> > aRowSeq( nRowCount );
125 uno::Sequence<double>* pRowAry = aRowSeq.getArray();
126 for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
127 {
128 uno::Sequence<double> aColSeq( nColCount );
129 double* pColAry = aColSeq.getArray();
130 for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
131 pColAry[nCol] = rDoc.GetValue(
132 ScAddress( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab ) );
133
134 pRowAry[nRow] = aColSeq;
135 }
136
137 rAny <<= aRowSeq;
138 return !lcl_HasErrors( rDoc, rRange );
139}
140
141bool ScRangeToSequence::FillDoubleArray( uno::Any& rAny, const ScMatrix* pMatrix )
142{
143 if (!pMatrix)
144 return false;
145
146 SCSIZE nColCount;
147 SCSIZE nRowCount;
148 pMatrix->GetDimensions( nColCount, nRowCount );
149
150 uno::Sequence< uno::Sequence<double> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
151 uno::Sequence<double>* pRowAry = aRowSeq.getArray();
152 for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
153 {
154 uno::Sequence<double> aColSeq( static_cast<sal_Int32>(nColCount) );
155 double* pColAry = aColSeq.getArray();
156 for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
157 if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
158 pColAry[nCol] = 0.0;
159 else
160 pColAry[nCol] = pMatrix->GetDouble( nCol, nRow );
161
162 pRowAry[nRow] = aColSeq;
163 }
164
165 rAny <<= aRowSeq;
166 return true;
167}
168
169bool ScRangeToSequence::FillStringArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange )
170{
171 SCTAB nTab = rRange.aStart.Tab();
172 SCCOL nStartCol = rRange.aStart.Col();
173 SCROW nStartRow = rRange.aStart.Row();
174 sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
175 sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
176
177 bool bHasErrors = false;
178
179 uno::Sequence< uno::Sequence<OUString> > aRowSeq( nRowCount );
180 uno::Sequence<OUString>* pRowAry = aRowSeq.getArray();
181 for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
182 {
183 uno::Sequence<OUString> aColSeq( nColCount );
184 OUString* pColAry = aColSeq.getArray();
185 for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
186 {
187 FormulaError nErrCode = rDoc.GetStringForFormula(
188 ScAddress(static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab),
189 pColAry[nCol] );
190 if ( nErrCode != FormulaError::NONE )
191 bHasErrors = true;
192 }
193 pRowAry[nRow] = aColSeq;
194 }
195
196 rAny <<= aRowSeq;
197 return !bHasErrors;
198}
199
200bool ScRangeToSequence::FillStringArray( uno::Any& rAny, const ScMatrix* pMatrix,
201 SvNumberFormatter* pFormatter )
202{
203 if (!pMatrix)
204 return false;
205
206 SCSIZE nColCount;
207 SCSIZE nRowCount;
208 pMatrix->GetDimensions( nColCount, nRowCount );
209
210 uno::Sequence< uno::Sequence<OUString> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
211 uno::Sequence<OUString>* pRowAry = aRowSeq.getArray();
212 for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
213 {
214 uno::Sequence<OUString> aColSeq( static_cast<sal_Int32>(nColCount) );
215 OUString* pColAry = aColSeq.getArray();
216 for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
217 {
218 OUString aStr;
219 if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
220 {
221 if ( !pMatrix->IsEmpty( nCol, nRow ) )
222 aStr = pMatrix->GetString(nCol, nRow).getString();
223 }
224 else if ( pFormatter )
225 {
226 double fVal = pMatrix->GetDouble( nCol, nRow );
227 const Color* pColor;
228 pFormatter->GetOutputString( fVal, 0, aStr, &pColor );
229 }
230 pColAry[nCol] = aStr;
231 }
232
233 pRowAry[nRow] = aColSeq;
234 }
235
236 rAny <<= aRowSeq;
237 return true;
238}
239
240bool ScRangeToSequence::FillMixedArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange,
241 bool bAllowNV )
242{
243 SCTAB nTab = rRange.aStart.Tab();
244 SCCOL nStartCol = rRange.aStart.Col();
245 SCROW nStartRow = rRange.aStart.Row();
246 sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
247 sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
248
249 bool bHasErrors = false;
250
251 uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( nRowCount );
252 uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray();
253 for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
254 {
255 uno::Sequence<uno::Any> aColSeq( nColCount );
256 uno::Any* pColAry = aColSeq.getArray();
257 for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
258 {
259 uno::Any& rElement = pColAry[nCol];
260
261 ScAddress aPos( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab );
262 ScRefCellValue aCell(rDoc, aPos);
263
264 if (aCell.isEmpty())
265 {
266 rElement <<= OUString();
267 continue;
268 }
269
270 if (aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->GetErrCode() != FormulaError::NONE)
271 {
272 // if NV is allowed, leave empty for errors
273 bHasErrors = true;
274 }
275 else if (aCell.hasNumeric())
276 rElement <<= aCell.getValue();
277 else
278 rElement <<= aCell.getString(&rDoc);
279 }
280 pRowAry[nRow] = aColSeq;
281 }
282
283 rAny <<= aRowSeq;
284 return bAllowNV || !bHasErrors;
285}
286
287bool ScRangeToSequence::FillMixedArray( uno::Any& rAny, const ScMatrix* pMatrix, bool bDataTypes )
288{
289 if (!pMatrix)
290 return false;
291
292 SCSIZE nColCount;
293 SCSIZE nRowCount;
294 pMatrix->GetDimensions( nColCount, nRowCount );
295
296 uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
297 uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray();
298 for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
299 {
300 uno::Sequence<uno::Any> aColSeq( static_cast<sal_Int32>(nColCount) );
301 uno::Any* pColAry = aColSeq.getArray();
302 for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
303 {
304 if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
305 {
306 OUString aStr;
307 if ( !pMatrix->IsEmpty( nCol, nRow ) )
308 aStr = pMatrix->GetString(nCol, nRow).getString();
309 pColAry[nCol] <<= aStr;
310 }
311 else
312 {
313 double fVal = pMatrix->GetDouble( nCol, nRow );
314 if (bDataTypes && pMatrix->IsBoolean( nCol, nRow ))
315 pColAry[nCol] <<= fVal != 0.0;
316 else
317 pColAry[nCol] <<= fVal;
318 }
319 }
320
321 pRowAry[nRow] = aColSeq;
322 }
323
324 rAny <<= aRowSeq;
325 return true;
326}
327
329 css::uno::TypeClass & o_eClass,
330 const css::uno::Any & rAny )
331{
332 bool bRet = false;
333 o_eClass = rAny.getValueTypeClass();
334 switch (o_eClass)
335 {
336 //TODO: extract integer values
337 case uno::TypeClass_ENUM:
338 case uno::TypeClass_BOOLEAN:
339 case uno::TypeClass_CHAR:
340 case uno::TypeClass_BYTE:
341 case uno::TypeClass_SHORT:
342 case uno::TypeClass_UNSIGNED_SHORT:
343 case uno::TypeClass_LONG:
344 case uno::TypeClass_UNSIGNED_LONG:
345 case uno::TypeClass_FLOAT:
346 case uno::TypeClass_DOUBLE:
347 rAny >>= o_fVal;
348 bRet = true;
349 break;
350 default:
351 ; // nothing, avoid warning
352 }
353 if (!bRet)
354 o_fVal = 0.0;
355 return bRet;
356}
357
359{
360 ScMatrixRef xMatrix;
361 uno::Sequence< uno::Sequence< uno::Any > > aSequence;
362 if ( rAny >>= aSequence )
363 {
364 sal_Int32 nRowCount = aSequence.getLength();
365 sal_Int32 nMaxColCount = 0;
366 if (nRowCount)
367 {
368 auto pRow = std::max_element(std::cbegin(aSequence), std::cend(aSequence),
369 [](const uno::Sequence<uno::Any>& a, const uno::Sequence<uno::Any>& b) {
370 return a.getLength() < b.getLength(); });
371 nMaxColCount = pRow->getLength();
372 }
373 if ( nMaxColCount && nRowCount )
374 {
375 const uno::Sequence<uno::Any>* pRowArr = aSequence.getConstArray();
376 OUString aUStr;
377 xMatrix = new ScMatrix(
378 static_cast<SCSIZE>(nMaxColCount),
379 static_cast<SCSIZE>(nRowCount), 0.0);
380 SCSIZE nCols, nRows;
381 xMatrix->GetDimensions( nCols, nRows);
382 if (nCols != static_cast<SCSIZE>(nMaxColCount) || nRows != static_cast<SCSIZE>(nRowCount))
383 {
384 OSL_FAIL( "ScSequenceToMatrix::CreateMixedMatrix: matrix exceeded max size, returning NULL matrix");
385 return nullptr;
386 }
387 for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
388 {
389 sal_Int32 nColCount = pRowArr[nRow].getLength();
390 const uno::Any* pColArr = pRowArr[nRow].getConstArray();
391 for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
392 {
393 double fVal;
394 uno::TypeClass eClass;
395 if (ScApiTypeConversion::ConvertAnyToDouble( fVal, eClass, pColArr[nCol]))
396 {
397 if (eClass == uno::TypeClass_BOOLEAN)
398 xMatrix->PutBoolean( fVal != 0.0,
399 static_cast<SCSIZE>(nCol),
400 static_cast<SCSIZE>(nRow) );
401 else
402 xMatrix->PutDouble( fVal,
403 static_cast<SCSIZE>(nCol),
404 static_cast<SCSIZE>(nRow) );
405 }
406 else
407 {
408 // Try string, else use empty as last resort.
409
410 if ( pColArr[nCol] >>= aUStr )
411 {
412 xMatrix->PutString(
413 svl::SharedString(aUStr), static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
414 }
415 else
416 xMatrix->PutEmpty(
417 static_cast<SCSIZE>(nCol),
418 static_cast<SCSIZE>(nRow) );
419 }
420 }
421 for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
422 {
423 xMatrix->PutEmpty(
424 static_cast<SCSIZE>(nCol),
425 static_cast<SCSIZE>(nRow) );
426 }
427 }
428 }
429 }
430 return xMatrix;
431}
432
433bool ScByteSequenceToString::GetString( OUString& rString, const uno::Any& rAny )
434{
435 bool bResult = false;
436 if (rAny >>= rString)
437 {
438 bResult = true;
439 }
440 else if (uno::Sequence<sal_Int8> aSeq; rAny >>= aSeq)
441 {
442 rString = OUString( reinterpret_cast<const char*>(aSeq.getConstArray()),
443 aSeq.getLength(), osl_getThreadTextEncoding() );
444 bResult = true;
445 }
446 if (bResult)
447 rString = comphelper::string::stripEnd(rString, 0);
448 return bResult;
449}
450
451/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
SCTAB Tab() const
Definition: address.hxx:283
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
static bool ConvertAnyToDouble(double &o_fVal, css::uno::TypeClass &o_eClass, const css::uno::Any &rAny)
Convert a uno::Any to double if possible, including integer types.
Definition: rangeseq.cxx:328
static bool GetString(OUString &rString, const css::uno::Any &rAny)
Definition: rangeseq.cxx:433
Walk through all cells in an area.
Definition: dociter.hxx:206
CellType getType() const
Definition: dociter.hxx:233
ScFormulaCell * getFormulaCell()
Definition: dociter.hxx:236
SC_DLLPUBLIC double GetValue(const ScAddress &rPos) const
Definition: document.cxx:3626
FormulaError GetStringForFormula(const ScAddress &rPos, OUString &rString)
Definition: document.cxx:3557
FormulaError GetErrCode()
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:101
svl::SharedString GetString(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3238
bool IsStringOrEmpty(SCSIZE nIndex) const
Definition: scmatrix.cxx:3258
bool IsEmpty(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3268
bool IsBoolean(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3303
double GetDouble(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3223
void GetDimensions(SCSIZE &rC, SCSIZE &rR) const
Definition: scmatrix.cxx:3143
static bool FillMixedArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange, bool bAllowNV=false)
static bool FillDoubleArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange)
static bool FillLongArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange)
static bool FillStringArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange)
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
static ScMatrixRef CreateMixedMatrix(const css::uno::Any &rAny)
Convert a sequence of mixed elements to ScMatrix.
Definition: rangeseq.cxx:358
void GetOutputString(const double &fOutNumber, sal_uInt32 nFIndex, OUString &sOutString, const Color **ppColor, bool bUseStarFormat=false)
const OUString & getString() const
FormulaError
@ CELLTYPE_FORMULA
Definition: global.hxx:276
uno_Any a
Sequence< sal_Int8 > aSeq
const long LONG_MAX
aStr
OString stripEnd(const OString &rIn, char c)
constexpr std::enable_if_t< std::is_floating_point_v< F > &&std::is_integral_v< I >, bool > convertsToAtLeast(F value, I min)
constexpr std::enable_if_t< std::is_floating_point_v< F > &&std::is_integral_v< I >, bool > convertsToAtMost(F value, I max)
long Long
static tools::Long lcl_DoubleToLong(double fVal)
Definition: rangeseq.cxx:53
static bool lcl_HasErrors(ScDocument &rDoc, const ScRange &rRange)
Definition: rangeseq.cxx:37
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:108
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
::boost::intrusive_ptr< ScMatrix > ScMatrixRef
Definition: types.hxx:25
sal_Int32 SCROW
Definition: types.hxx:17