diff options
Diffstat (limited to 'sc/source/core/tool/rangeseq.cxx')
-rw-r--r-- | sc/source/core/tool/rangeseq.cxx | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/sc/source/core/tool/rangeseq.cxx b/sc/source/core/tool/rangeseq.cxx new file mode 100644 index 000000000..909f5f3af --- /dev/null +++ b/sc/source/core/tool/rangeseq.cxx @@ -0,0 +1,446 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/zforlist.hxx> +#include <rtl/math.hxx> +#include <o3tl/float_int_conversion.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <comphelper/string.hxx> +#include <rangeseq.hxx> +#include <document.hxx> +#include <dociter.hxx> +#include <scmatrix.hxx> +#include <formulacell.hxx> + +using namespace com::sun::star; + +static bool lcl_HasErrors( ScDocument* pDoc, const ScRange& rRange ) +{ + // no need to look at empty cells - just use ScCellIterator + ScCellIterator aIter( pDoc, rRange ); + for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) + { + if (aIter.getType() != CELLTYPE_FORMULA) + continue; + + ScFormulaCell* pCell = aIter.getFormulaCell(); + if (pCell->GetErrCode() != FormulaError::NONE) + return true; + } + return false; // no error found +} + +static long lcl_DoubleToLong( double fVal ) +{ + double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) : + ::rtl::math::approxCeil( fVal ); + if ( o3tl::convertsToAtLeast(fInt, LONG_MIN) && o3tl::convertsToAtMost(fInt, LONG_MAX) ) + return static_cast<long>(fInt); + else + return 0; // out of range +} + +bool ScRangeToSequence::FillLongArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + uno::Sequence< uno::Sequence<sal_Int32> > aRowSeq( nRowCount ); + uno::Sequence<sal_Int32>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<sal_Int32> aColSeq( nColCount ); + sal_Int32* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + pColAry[nCol] = lcl_DoubleToLong( pDoc->GetValue( + ScAddress( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab ) ) ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return !lcl_HasErrors( pDoc, rRange ); +} + +bool ScRangeToSequence::FillLongArray( uno::Any& rAny, const ScMatrix* pMatrix ) +{ + if (!pMatrix) + return false; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<sal_Int32> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<sal_Int32>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<sal_Int32> aColSeq( static_cast<sal_Int32>(nColCount) ); + sal_Int32* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + if ( pMatrix->IsStringOrEmpty( nCol, nRow ) ) + pColAry[nCol] = 0; + else + pColAry[nCol] = lcl_DoubleToLong( pMatrix->GetDouble( nCol, nRow ) ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return true; +} + +bool ScRangeToSequence::FillDoubleArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + uno::Sequence< uno::Sequence<double> > aRowSeq( nRowCount ); + uno::Sequence<double>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<double> aColSeq( nColCount ); + double* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + pColAry[nCol] = pDoc->GetValue( + ScAddress( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab ) ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return !lcl_HasErrors( pDoc, rRange ); +} + +bool ScRangeToSequence::FillDoubleArray( uno::Any& rAny, const ScMatrix* pMatrix ) +{ + if (!pMatrix) + return false; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<double> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<double>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<double> aColSeq( static_cast<sal_Int32>(nColCount) ); + double* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + if ( pMatrix->IsStringOrEmpty( nCol, nRow ) ) + pColAry[nCol] = 0.0; + else + pColAry[nCol] = pMatrix->GetDouble( nCol, nRow ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return true; +} + +bool ScRangeToSequence::FillStringArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + bool bHasErrors = false; + + uno::Sequence< uno::Sequence<OUString> > aRowSeq( nRowCount ); + uno::Sequence<OUString>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<OUString> aColSeq( nColCount ); + OUString* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + { + FormulaError nErrCode = pDoc->GetStringForFormula( + ScAddress(static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab), + pColAry[nCol] ); + if ( nErrCode != FormulaError::NONE ) + bHasErrors = true; + } + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return !bHasErrors; +} + +bool ScRangeToSequence::FillStringArray( uno::Any& rAny, const ScMatrix* pMatrix, + SvNumberFormatter* pFormatter ) +{ + if (!pMatrix) + return false; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<OUString> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<OUString>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<OUString> aColSeq( static_cast<sal_Int32>(nColCount) ); + OUString* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + { + OUString aStr; + if ( pMatrix->IsStringOrEmpty( nCol, nRow ) ) + { + if ( !pMatrix->IsEmpty( nCol, nRow ) ) + aStr = pMatrix->GetString(nCol, nRow).getString(); + } + else if ( pFormatter ) + { + double fVal = pMatrix->GetDouble( nCol, nRow ); + Color* pColor; + pFormatter->GetOutputString( fVal, 0, aStr, &pColor ); + } + pColAry[nCol] = aStr; + } + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return true; +} + +bool ScRangeToSequence::FillMixedArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange, + bool bAllowNV ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + bool bHasErrors = false; + + uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( nRowCount ); + uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<uno::Any> aColSeq( nColCount ); + uno::Any* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + { + uno::Any& rElement = pColAry[nCol]; + + ScAddress aPos( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab ); + ScRefCellValue aCell(*pDoc, aPos); + + if (aCell.isEmpty()) + { + rElement <<= EMPTY_OUSTRING; + continue; + } + + if (aCell.meType == CELLTYPE_FORMULA && aCell.mpFormula->GetErrCode() != FormulaError::NONE) + { + // if NV is allowed, leave empty for errors + bHasErrors = true; + } + else if (aCell.hasNumeric()) + rElement <<= aCell.getValue(); + else + rElement <<= aCell.getString(pDoc); + } + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return bAllowNV || !bHasErrors; +} + +bool ScRangeToSequence::FillMixedArray( uno::Any& rAny, const ScMatrix* pMatrix, bool bDataTypes ) +{ + if (!pMatrix) + return false; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<uno::Any> aColSeq( static_cast<sal_Int32>(nColCount) ); + uno::Any* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + { + if ( pMatrix->IsStringOrEmpty( nCol, nRow ) ) + { + OUString aStr; + if ( !pMatrix->IsEmpty( nCol, nRow ) ) + aStr = pMatrix->GetString(nCol, nRow).getString(); + pColAry[nCol] <<= aStr; + } + else + { + double fVal = pMatrix->GetDouble( nCol, nRow ); + if (bDataTypes && pMatrix->IsBoolean( nCol, nRow )) + pColAry[nCol] <<= fVal != 0.0; + else + pColAry[nCol] <<= fVal; + } + } + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return true; +} + +bool ScApiTypeConversion::ConvertAnyToDouble( double & o_fVal, + css::uno::TypeClass & o_eClass, + const css::uno::Any & rAny ) +{ + bool bRet = false; + o_eClass = rAny.getValueTypeClass(); + switch (o_eClass) + { + //TODO: extract integer values + case uno::TypeClass_ENUM: + case uno::TypeClass_BOOLEAN: + case uno::TypeClass_CHAR: + case uno::TypeClass_BYTE: + case uno::TypeClass_SHORT: + case uno::TypeClass_UNSIGNED_SHORT: + case uno::TypeClass_LONG: + case uno::TypeClass_UNSIGNED_LONG: + case uno::TypeClass_FLOAT: + case uno::TypeClass_DOUBLE: + rAny >>= o_fVal; + bRet = true; + break; + default: + ; // nothing, avoid warning + } + if (!bRet) + o_fVal = 0.0; + return bRet; +} + +ScMatrixRef ScSequenceToMatrix::CreateMixedMatrix( const css::uno::Any & rAny ) +{ + ScMatrixRef xMatrix; + uno::Sequence< uno::Sequence< uno::Any > > aSequence; + if ( rAny >>= aSequence ) + { + sal_Int32 nRowCount = aSequence.getLength(); + sal_Int32 nMaxColCount = 0; + if (nRowCount) + { + auto pRow = std::max_element(aSequence.begin(), aSequence.end(), + [](const uno::Sequence<uno::Any>& a, const uno::Sequence<uno::Any>& b) { + return a.getLength() < b.getLength(); }); + nMaxColCount = pRow->getLength(); + } + if ( nMaxColCount && nRowCount ) + { + const uno::Sequence<uno::Any>* pRowArr = aSequence.getConstArray(); + OUString aUStr; + xMatrix = new ScMatrix( + static_cast<SCSIZE>(nMaxColCount), + static_cast<SCSIZE>(nRowCount), 0.0); + SCSIZE nCols, nRows; + xMatrix->GetDimensions( nCols, nRows); + if (nCols != static_cast<SCSIZE>(nMaxColCount) || nRows != static_cast<SCSIZE>(nRowCount)) + { + OSL_FAIL( "ScSequenceToMatrix::CreateMixedMatrix: matrix exceeded max size, returning NULL matrix"); + return nullptr; + } + for (sal_Int32 nRow=0; nRow<nRowCount; nRow++) + { + sal_Int32 nColCount = pRowArr[nRow].getLength(); + const uno::Any* pColArr = pRowArr[nRow].getConstArray(); + for (sal_Int32 nCol=0; nCol<nColCount; nCol++) + { + double fVal; + uno::TypeClass eClass; + if (ScApiTypeConversion::ConvertAnyToDouble( fVal, eClass, pColArr[nCol])) + { + if (eClass == uno::TypeClass_BOOLEAN) + xMatrix->PutBoolean( fVal != 0.0, + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + else + xMatrix->PutDouble( fVal, + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + else + { + // Try string, else use empty as last resort. + + if ( pColArr[nCol] >>= aUStr ) + { + xMatrix->PutString( + svl::SharedString(aUStr), static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow)); + } + else + xMatrix->PutEmpty( + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + } + for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++) + { + xMatrix->PutEmpty( + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + } + } + } + return xMatrix; +} + +bool ScByteSequenceToString::GetString( OUString& rString, const uno::Any& rAny, + sal_uInt16 nEncoding ) +{ + uno::Sequence<sal_Int8> aSeq; + if ( rAny >>= aSeq ) + { + rString = OUString( reinterpret_cast<const char*>(aSeq.getConstArray()), + aSeq.getLength(), nEncoding ); + rString = comphelper::string::stripEnd(rString, 0); + return true; + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |