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/zforlist.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 
34 using namespace com::sun::star;
35 
36 static 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 
52 static 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<long>(fInt);
58  else
59  return 0; // out of range
60 }
61 
62 bool 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  long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
68  long 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 (long nRow = 0; nRow < nRowCount; nRow++)
73  {
74  uno::Sequence<sal_Int32> aColSeq( nColCount );
75  sal_Int32* pColAry = aColSeq.getArray();
76  for (long 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 
87 bool 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 
115 bool 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  long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
121  long 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 (long nRow = 0; nRow < nRowCount; nRow++)
126  {
127  uno::Sequence<double> aColSeq( nColCount );
128  double* pColAry = aColSeq.getArray();
129  for (long 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 
140 bool 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 
168 bool 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  long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
174  long 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 (long nRow = 0; nRow < nRowCount; nRow++)
181  {
182  uno::Sequence<OUString> aColSeq( nColCount );
183  OUString* pColAry = aColSeq.getArray();
184  for (long 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 
199 bool 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 
239 bool 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  long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
246  long 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 (long nRow = 0; nRow < nRowCount; nRow++)
253  {
254  uno::Sequence<uno::Any> aColSeq( nColCount );
255  uno::Any* pColAry = aColSeq.getArray();
256  for (long 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 <<= EMPTY_OUSTRING;
266  continue;
267  }
268 
269  if (aCell.meType == CELLTYPE_FORMULA && aCell.mpFormula->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 
286 bool 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(aSequence.begin(), aSequence.end(),
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 
432 bool 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: */
OString stripEnd(const OString &rIn, char c)
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:113
const int nColCount
static bool FillLongArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange)
OUString getString() const
ScAddress aStart
Definition: address.hxx:500
#define EMPTY_OUSTRING
Definition: global.hxx:214
static bool lcl_HasErrors(ScDocument &rDoc, const ScRange &rRange)
Definition: rangeseq.cxx:36
SCROW Row() const
Definition: address.hxx:262
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
ScAddress aEnd
Definition: address.hxx:501
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:104
ScFormulaCell * getFormulaCell()
Definition: dociter.hxx:239
double GetDouble(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3064
CellType getType() const
Definition: dociter.hxx:236
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:45
FormulaError GetErrCode()
::boost::intrusive_ptr< ScMatrix > ScMatrixRef
Definition: types.hxx:26
Walk through all cells in an area.
Definition: dociter.hxx:208
SCTAB Tab() const
Definition: address.hxx:271
bool IsStringOrEmpty(SCSIZE nIndex) const
Definition: scmatrix.cxx:3099
static bool FillMixedArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange, bool bAllowNV=false)
static long lcl_DoubleToLong(double fVal)
Definition: rangeseq.cxx:52
static bool FillDoubleArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange)
SC_DLLPUBLIC double GetValue(const ScAddress &rPos) const
Definition: document.cxx:3628
static bool FillStringArray(css::uno::Any &rAny, ScDocument &rDoc, const ScRange &rRange)
svl::SharedString GetString(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3079
uno_Any a
void GetDimensions(SCSIZE &rC, SCSIZE &rR) const
Definition: scmatrix.cxx:2984
sal_Int16 SCCOL
Definition: types.hxx:22
bool IsBoolean(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3144
std::enable_if_t< std::is_floating_point_v< F > &&std::is_integral_v< I >, bool > convertsToAtMost(F value, I max)
const long LONG_MAX
FormulaError
SCCOL Col() const
Definition: address.hxx:267
static bool GetString(OUString &rString, const css::uno::Any &rAny, sal_uInt16 nEncoding)
Definition: rangeseq.cxx:432
sal_Int32 SCROW
Definition: types.hxx:18
bool IsEmpty(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3109
static ScMatrixRef CreateMixedMatrix(const css::uno::Any &rAny)
Convert a sequence of mixed elements to ScMatrix.
Definition: rangeseq.cxx:357
std::enable_if_t< std::is_floating_point_v< F > &&std::is_integral_v< I >, bool > convertsToAtLeast(F value, I min)
Sequence< sal_Int8 > aSeq
FormulaError GetStringForFormula(const ScAddress &rPos, OUString &rString)
Definition: document.cxx:3548
aStr
void GetOutputString(const double &fOutNumber, sal_uInt32 nFIndex, OUString &sOutString, const Color **ppColor, bool bUseStarFormat=false)
sal_Int16 SCTAB
Definition: types.hxx:23