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