summaryrefslogtreecommitdiffstats
path: root/chart2/source/tools/InternalDataProvider.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'chart2/source/tools/InternalDataProvider.cxx')
-rw-r--r--chart2/source/tools/InternalDataProvider.cxx1553
1 files changed, 1553 insertions, 0 deletions
diff --git a/chart2/source/tools/InternalDataProvider.cxx b/chart2/source/tools/InternalDataProvider.cxx
new file mode 100644
index 000000000..0c95286e9
--- /dev/null
+++ b/chart2/source/tools/InternalDataProvider.cxx
@@ -0,0 +1,1553 @@
+/* -*- 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 <cstddef>
+#include <iterator>
+
+#include <InternalDataProvider.hxx>
+#include <LabeledDataSequence.hxx>
+#include <DataSource.hxx>
+#include <XMLRangeHelper.hxx>
+#include <CommonFunctors.hxx>
+#include <UncachedDataSequence.hxx>
+#include <DataSourceHelper.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <ExplicitCategoriesProvider.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <DataSeries.hxx>
+
+#include <com/sun/star/chart2/data/XDataSequence.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/property.hxx>
+#include <o3tl/string_view.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <limits>
+#include <vector>
+#include <algorithm>
+
+namespace com::sun::star::chart2 { class XChartDocument; }
+
+using namespace ::com::sun::star;
+using namespace ::std;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+namespace
+{
+
+constexpr OUStringLiteral lcl_aCategoriesRangeName = u"categories";
+const char lcl_aCategoriesLevelRangeNamePrefix[] = "categoriesL "; //L <-> level
+const char lcl_aCategoriesPointRangeNamePrefix[] = "categoriesP "; //P <-> point
+constexpr OUStringLiteral lcl_aCategoriesRoleName = u"categories";
+const char lcl_aLabelRangePrefix[] = "label ";
+constexpr OUStringLiteral lcl_aCompleteRange = u"all";
+
+typedef std::multimap< OUString, uno::WeakReference< chart2::data::XDataSequence > >
+ lcl_tSequenceMap;
+
+std::vector< OUString > lcl_AnyToStringSequence( const std::vector< uno::Any >& aAnySeq )
+{
+ std::vector< OUString > aResult;
+ aResult.resize( aAnySeq.size() );
+ int i = 0;
+ for (const uno::Any& aAny : aAnySeq)
+ aResult[i++] = CommonFunctors::AnyToString()(aAny);
+ return aResult;
+}
+
+std::vector< uno::Any > lcl_StringToAnyVector( const css::uno::Sequence< OUString >& aStringSeq )
+{
+ std::vector< uno::Any > aResult;
+ aResult.resize( aStringSeq.getLength() );
+ int i = 0;
+ for (const OUString& aStr : aStringSeq)
+ aResult[i++] = CommonFunctors::makeAny<OUString>()(aStr);
+ return aResult;
+}
+
+struct lcl_setModified
+{
+ void operator() ( const lcl_tSequenceMap::value_type & rMapEntry )
+ {
+ // convert weak reference to reference
+ Reference< chart2::data::XDataSequence > xSeq( rMapEntry.second );
+ if( xSeq.is())
+ {
+ Reference< util::XModifiable > xMod( xSeq, uno::UNO_QUERY );
+ if( xMod.is())
+ xMod->setModified( true );
+ }
+ }
+};
+
+struct lcl_internalizeSeries
+{
+ lcl_internalizeSeries( InternalData & rInternalData,
+ InternalDataProvider & rProvider,
+ bool bConnectToModel, bool bDataInColumns ) :
+ m_rInternalData( rInternalData ),
+ m_rProvider( rProvider ),
+ m_bConnectToModel( bConnectToModel ),
+ m_bDataInColumns( bDataInColumns )
+ {}
+ void operator() ( const rtl::Reference< DataSeries > & xSeries )
+ {
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aOldSeriesData = xSeries->getDataSequences2();
+ std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aNewSeriesData( aOldSeriesData.size() );
+ for( std::size_t i=0; i<aOldSeriesData.size(); ++i )
+ {
+ sal_Int32 nNewIndex( m_bDataInColumns ? m_rInternalData.appendColumn() : m_rInternalData.appendRow() );
+ OUString aIdentifier( OUString::number( nNewIndex ));
+ //@todo: deal also with genericXDataSequence
+ Reference< chart2::data::XNumericalDataSequence > xValues( aOldSeriesData[i]->getValues(), uno::UNO_QUERY );
+ Reference< chart2::data::XTextualDataSequence > xLabel( aOldSeriesData[i]->getLabel(), uno::UNO_QUERY );
+ Reference< chart2::data::XDataSequence > xNewValues;
+
+ if( xValues.is() )
+ {
+ auto aValues( comphelper::sequenceToContainer<std::vector< double >>( xValues->getNumericalData()));
+ if( m_bDataInColumns )
+ m_rInternalData.setColumnValues( nNewIndex, aValues );
+ else
+ m_rInternalData.setRowValues( nNewIndex, aValues );
+ if( m_bConnectToModel )
+ {
+ xNewValues.set( m_rProvider.createDataSequenceByRangeRepresentation( aIdentifier ));
+ comphelper::copyProperties(
+ Reference< beans::XPropertySet >( xValues, uno::UNO_QUERY ),
+ Reference< beans::XPropertySet >( xNewValues, uno::UNO_QUERY ));
+ }
+ }
+
+ if( xLabel.is() )
+ {
+ if( m_bDataInColumns )
+ m_rInternalData.setComplexColumnLabel( nNewIndex, lcl_StringToAnyVector( xLabel->getTextualData() ) );
+ else
+ m_rInternalData.setComplexRowLabel( nNewIndex, lcl_StringToAnyVector( xLabel->getTextualData() ) );
+ if( m_bConnectToModel )
+ {
+ Reference< chart2::data::XDataSequence > xNewLabel(
+ m_rProvider.createDataSequenceByRangeRepresentation( lcl_aLabelRangePrefix + aIdentifier ));
+ comphelper::copyProperties(
+ Reference< beans::XPropertySet >( xLabel, uno::UNO_QUERY ),
+ Reference< beans::XPropertySet >( xNewLabel, uno::UNO_QUERY ));
+ aNewSeriesData[i].set( new LabeledDataSequence( xNewValues, xNewLabel ) );
+ }
+ }
+ else
+ {
+ if( m_bConnectToModel )
+ aNewSeriesData[i].set( new LabeledDataSequence( xNewValues ) );
+ }
+ }
+ if( m_bConnectToModel )
+ xSeries->setData( aNewSeriesData );
+ }
+
+private:
+ InternalData & m_rInternalData;
+ InternalDataProvider & m_rProvider;
+ bool m_bConnectToModel;
+ bool m_bDataInColumns;
+};
+
+struct lcl_copyFromLevel
+{
+public:
+
+ explicit lcl_copyFromLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ uno::Any operator() ( const vector< uno::Any >& rVector )
+ {
+ uno::Any aRet;
+ if( m_nLevel < static_cast< sal_Int32 >(rVector.size()) )
+ aRet = rVector[m_nLevel];
+ return aRet;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_getStringFromLevelVector
+{
+public:
+
+ explicit lcl_getStringFromLevelVector( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ OUString operator() ( const vector< uno::Any >& rVector )
+ {
+ OUString aString;
+ if( m_nLevel < static_cast< sal_Int32 >(rVector.size()) )
+ aString = CommonFunctors::AnyToString()(rVector[m_nLevel]);
+ return aString;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_setAnyAtLevel
+{
+public:
+
+ explicit lcl_setAnyAtLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ vector< uno::Any > operator() ( const vector< uno::Any >& rVector, const uno::Any& rNewValue )
+ {
+ vector< uno::Any > aRet( rVector );
+ if( m_nLevel >= static_cast< sal_Int32 >(aRet.size()) )
+ aRet.resize( m_nLevel+1 );
+ aRet[ m_nLevel ]=rNewValue;
+ return aRet;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_setAnyAtLevelFromStringSequence
+{
+public:
+
+ explicit lcl_setAnyAtLevelFromStringSequence( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ vector< uno::Any > operator() ( const vector< uno::Any >& rVector, const OUString& rNewValue )
+ {
+ vector< uno::Any > aRet( rVector );
+ if( m_nLevel >= static_cast< sal_Int32 >(aRet.size()) )
+ aRet.resize( m_nLevel+1 );
+ aRet[ m_nLevel ] <<= rNewValue;
+ return aRet;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_insertAnyAtLevel
+{
+public:
+
+ explicit lcl_insertAnyAtLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ void operator() ( vector< uno::Any >& rVector )
+ {
+ if( m_nLevel >= static_cast< sal_Int32 >(rVector.size()) )
+ {
+ rVector.resize( m_nLevel + 1 );
+ }
+ else
+ {
+ rVector.insert( rVector.begin() + m_nLevel, uno::Any() );
+ }
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_removeAnyAtLevel
+{
+public:
+
+ explicit lcl_removeAnyAtLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ void operator() ( vector< uno::Any >& rVector )
+ {
+ if( m_nLevel < static_cast<sal_Int32>(rVector.size()) )
+ {
+ rVector.erase(rVector.begin() + m_nLevel);
+ }
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+} // anonymous namespace
+
+InternalDataProvider::InternalDataProvider()
+ : m_bDataInColumns( true )
+{}
+
+InternalDataProvider::InternalDataProvider(
+ const rtl::Reference< ChartModel > & xModel,
+ bool bConnectToModel,
+ bool bDefaultDataInColumns)
+: m_bDataInColumns( bDefaultDataInColumns )
+{
+ if (!xModel.is())
+ return;
+ try
+ {
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xModel ) );
+ if( xDiagram.is())
+ {
+ //data in columns?
+ {
+ OUString aRangeString;
+ bool bFirstCellAsLabel = true;
+ bool bHasCategories = true;
+ uno::Sequence< sal_Int32 > aSequenceMapping;
+ const bool bSomethingDetected(
+ DataSourceHelper::detectRangeSegmentation(
+ xModel, aRangeString, aSequenceMapping, m_bDataInColumns, bFirstCellAsLabel, bHasCategories ));
+
+ // #i120559# if no data was available, restore default
+ if(!bSomethingDetected && m_bDataInColumns != bDefaultDataInColumns)
+ {
+ m_bDataInColumns = bDefaultDataInColumns;
+ }
+ }
+
+ // categories
+ {
+ vector< vector< uno::Any > > aNewCategories;//inner count is level
+ {
+ ExplicitCategoriesProvider aExplicitCategoriesProvider(ChartModelHelper::getFirstCoordinateSystem(xModel), *xModel);
+
+ const std::vector< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList( aExplicitCategoriesProvider.getSplitCategoriesList() );
+ sal_Int32 nLevelCount = rSplitCategoriesList.size();
+ for( sal_Int32 nL = 0; nL<nLevelCount; nL++ )
+ {
+ Reference< chart2::data::XLabeledDataSequence > xLDS( rSplitCategoriesList[nL] );
+ if( !xLDS.is() )
+ continue;
+ Sequence< uno::Any > aDataSeq;
+ Reference< chart2::data::XDataSequence > xSeq( xLDS->getValues() );
+ if( xSeq.is() )
+ aDataSeq = xSeq->getData();
+ sal_Int32 nLength = aDataSeq.getLength();
+ sal_Int32 nCatLength = static_cast< sal_Int32 >(aNewCategories.size());
+ if( nCatLength < nLength )
+ aNewCategories.resize( nLength );
+ else if( nLength < nCatLength )
+ aDataSeq.realloc( nCatLength );
+ transform( aNewCategories.begin(), aNewCategories.end(), aDataSeq.getConstArray(),
+ aNewCategories.begin(), lcl_setAnyAtLevel(nL) );
+ }
+ if( !nLevelCount )
+ {
+ Sequence< OUString > aSimplecategories = aExplicitCategoriesProvider.getSimpleCategories();
+ sal_Int32 nLength = aSimplecategories.getLength();
+ aNewCategories.reserve( nLength );
+ for( sal_Int32 nN=0; nN<nLength; nN++)
+ {
+ aNewCategories.push_back( { uno::Any(aSimplecategories[nN]) } );
+ }
+ }
+ }
+
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aNewCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aNewCategories) );
+ if( bConnectToModel )
+ DiagramHelper::setCategoriesToDiagram(
+ rtl::Reference< LabeledDataSequence >(new LabeledDataSequence(
+ createDataSequenceByRangeRepresentation( lcl_aCategoriesRangeName ))),
+ xDiagram );
+ }
+
+ // data series
+ std::vector< rtl::Reference< DataSeries > > aSeriesVector( ChartModelHelper::getDataSeries( xModel ));
+ lcl_internalizeSeries ftor( m_aInternalData, *this, bConnectToModel, m_bDataInColumns );
+ for( const auto& rxScreen : aSeriesVector )
+ ftor( rxScreen );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+// copy-CTOR
+InternalDataProvider::InternalDataProvider( const InternalDataProvider & rOther ) :
+ impl::InternalDataProvider_Base(rOther),
+ m_aSequenceMap( rOther.m_aSequenceMap ),
+ m_aInternalData( rOther.m_aInternalData ),
+ m_bDataInColumns( rOther.m_bDataInColumns )
+{}
+
+InternalDataProvider::~InternalDataProvider()
+{}
+
+void InternalDataProvider::addDataSequenceToMap(
+ const OUString & rRangeRepresentation,
+ const Reference< chart2::data::XDataSequence > & xSequence )
+{
+ m_aSequenceMap.emplace(
+ rRangeRepresentation,
+ uno::WeakReference< chart2::data::XDataSequence >( xSequence ));
+}
+
+void InternalDataProvider::deleteMapReferences( const OUString & rRangeRepresentation )
+{
+ // set sequence to deleted by setting its range to an empty string
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( rRangeRepresentation ));
+ for( tSequenceMap::iterator aIt( aRange.first ); aIt != aRange.second; ++aIt )
+ {
+ Reference< chart2::data::XDataSequence > xSeq( aIt->second );
+ if( xSeq.is())
+ {
+ Reference< container::XNamed > xNamed( xSeq, uno::UNO_QUERY );
+ if( xNamed.is())
+ xNamed->setName( OUString());
+ }
+ }
+ // remove from map
+ m_aSequenceMap.erase( aRange.first, aRange.second );
+}
+
+void InternalDataProvider::adaptMapReferences(
+ const OUString & rOldRangeRepresentation,
+ const OUString & rNewRangeRepresentation )
+{
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( rOldRangeRepresentation ));
+ tSequenceMap aNewElements;
+ for( tSequenceMap::iterator aIt( aRange.first ); aIt != aRange.second; ++aIt )
+ {
+ Reference< chart2::data::XDataSequence > xSeq( aIt->second );
+ if( xSeq.is())
+ {
+ Reference< container::XNamed > xNamed( xSeq, uno::UNO_QUERY );
+ if( xNamed.is())
+ xNamed->setName( rNewRangeRepresentation );
+ }
+ aNewElements.emplace( rNewRangeRepresentation, aIt->second );
+ }
+ // erase map values for old index
+ m_aSequenceMap.erase( aRange.first, aRange.second );
+ // add new entries for values with new index
+ m_aSequenceMap.insert( aNewElements.begin(), aNewElements.end() );
+}
+
+void InternalDataProvider::increaseMapReferences(
+ sal_Int32 nBegin, sal_Int32 nEnd )
+{
+ for( sal_Int32 nIndex = nEnd - 1; nIndex >= nBegin; --nIndex )
+ {
+ adaptMapReferences( OUString::number( nIndex ),
+ OUString::number( nIndex + 1 ));
+ adaptMapReferences( lcl_aLabelRangePrefix + OUString::number( nIndex ),
+ lcl_aLabelRangePrefix + OUString::number( nIndex + 1 ));
+ }
+}
+
+void InternalDataProvider::decreaseMapReferences(
+ sal_Int32 nBegin, sal_Int32 nEnd )
+{
+ for( sal_Int32 nIndex = nBegin; nIndex < nEnd; ++nIndex )
+ {
+ adaptMapReferences( OUString::number( nIndex ),
+ OUString::number( nIndex - 1 ));
+ adaptMapReferences( lcl_aLabelRangePrefix + OUString::number( nIndex ),
+ lcl_aLabelRangePrefix + OUString::number( nIndex - 1 ));
+ }
+}
+
+rtl::Reference< UncachedDataSequence > InternalDataProvider::createDataSequenceAndAddToMap(
+ const OUString & rRangeRepresentation )
+{
+ rtl::Reference<UncachedDataSequence> xSeq = createDataSequenceFromArray(rRangeRepresentation, u"", u"");
+ if (xSeq.is())
+ return nullptr;
+
+ xSeq.set(new UncachedDataSequence(this, rRangeRepresentation));
+ addDataSequenceToMap(rRangeRepresentation, xSeq);
+ return xSeq;
+}
+
+rtl::Reference<UncachedDataSequence>
+InternalDataProvider::createDataSequenceFromArray( const OUString& rArrayStr, std::u16string_view rRole, std::u16string_view rRoleQualifier )
+{
+ if (rArrayStr.indexOf('{') != 0 || rArrayStr[rArrayStr.getLength()-1] != '}')
+ {
+ // Not an array string.
+ return nullptr;
+ }
+
+ bool bAllNumeric = true;
+ rtl::Reference<UncachedDataSequence> xSeq;
+
+ const sal_Unicode* p = rArrayStr.getStr();
+ const sal_Unicode* pEnd = p + rArrayStr.getLength();
+ const sal_Unicode* pElem = nullptr;
+ OUString aElem;
+
+ std::vector<OUString> aRawElems;
+ ++p; // Skip the first '{'.
+ --pEnd; // Skip the last '}'.
+ bool bInQuote = false;
+ for (; p != pEnd; ++p)
+ {
+ // Skip next "" within the title text: it's an escaped double quotation mark.
+ if (bInQuote && *p == '"' && *(p + 1) == '"')
+ {
+ if (!pElem)
+ pElem = p;
+ ++p;
+ }
+ else if (*p == '"')
+ {
+ bInQuote = !bInQuote;
+ if (bInQuote)
+ {
+ // Opening quote.
+ pElem = nullptr;
+ }
+ else
+ {
+ // Closing quote.
+ if (pElem)
+ aElem = OUString(pElem, p-pElem);
+ // Non empty string
+ if (!aElem.isEmpty())
+ bAllNumeric = false;
+ // Restore also escaped double quotation marks
+ aRawElems.push_back(aElem.replaceAll("\"\"", "\""));
+ pElem = nullptr;
+ aElem.clear();
+
+ ++p; // Skip '"'.
+ if (p == pEnd)
+ break;
+ }
+ }
+ else if (*p == ';' && !bInQuote)
+ {
+ // element separator.
+ if (pElem)
+ aElem = OUString(pElem, p-pElem);
+ aRawElems.push_back(aElem);
+ pElem = nullptr;
+ aElem.clear();
+ }
+ else if (!pElem)
+ pElem = p;
+ }
+
+ if (pElem)
+ {
+ aElem = OUString(pElem, p-pElem);
+ aRawElems.push_back(aElem);
+ }
+
+ if (rRole == u"values-y" || rRole == u"values-first" || rRole == u"values-last" ||
+ rRole == u"values-min" || rRole == u"values-max" || rRole == u"values-size" ||
+ rRole == u"error-bars-y-positive" || rRole == u"error-bars-y-negative")
+ {
+ // Column values. Append a new data column and populate it.
+
+ std::vector<double> aValues;
+ aValues.reserve(aRawElems.size());
+ for (const OUString & aRawElem : aRawElems)
+ {
+ if (aRawElem.isEmpty())
+ aValues.push_back(NAN);
+ else
+ aValues.push_back(aRawElem.toDouble());
+ }
+ sal_Int32 n = m_aInternalData.appendColumn();
+
+ m_aInternalData.setColumnValues(n, aValues);
+
+ OUString aRangeRep = OUString::number(n);
+ xSeq.set(new UncachedDataSequence(this, aRangeRep));
+ addDataSequenceToMap(aRangeRep, xSeq);
+ }
+ else if (rRole == u"values-x")
+ {
+ std::vector<double> aValues;
+ aValues.reserve(aRawElems.size());
+ if (bAllNumeric)
+ {
+ for (const OUString & aRawElem : aRawElems)
+ {
+ if (!aRawElem.isEmpty())
+ aValues.push_back(aRawElem.toDouble());
+ else
+ aValues.push_back(NAN);
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < aRawElems.size(); ++i)
+ aValues.push_back(i+1);
+ }
+
+ sal_Int32 n = m_aInternalData.appendColumn();
+ m_aInternalData.setColumnValues(n, aValues);
+
+ OUString aRangeRep = OUString::number(n);
+ xSeq.set(new UncachedDataSequence(this, aRangeRep));
+ addDataSequenceToMap(aRangeRep, xSeq);
+ }
+ else if (rRole == u"categories")
+ {
+ // Category labels.
+
+ // Store date categories as numbers.
+ bool bStoreNumeric = rRoleQualifier == u"date";
+ double fValue;
+ for (size_t i = 0; i < aRawElems.size(); ++i)
+ {
+ if (bStoreNumeric)
+ {
+ bool bGetDouble = bAllNumeric && !aRawElems[i].isEmpty();
+ fValue = bGetDouble ? aRawElems[i].toDouble() :
+ std::numeric_limits<double>::quiet_NaN();
+ }
+ std::vector<uno::Any> aLabels(1,
+ bStoreNumeric ? uno::Any(fValue) : uno::Any(aRawElems[i]));
+ m_aInternalData.setComplexRowLabel(i, std::move(aLabels));
+ }
+
+ xSeq.set(new UncachedDataSequence(this, lcl_aCategoriesRangeName));
+ addDataSequenceToMap(lcl_aCategoriesRangeName, xSeq);
+ }
+ else if (rRole == u"label")
+ {
+ // Data series label. There should be only one element. This always
+ // goes to the last data column.
+ sal_Int32 nColSize = m_aInternalData.getColumnCount();
+ if (!aRawElems.empty() && nColSize)
+ {
+ // Do not overwrite an existing label (attempted by series with no data values)
+ if (!m_aInternalData.getComplexColumnLabel(nColSize-1)[0].hasValue())
+ {
+ std::vector<uno::Any> aLabels(1, uno::Any(aRawElems[0]));
+ m_aInternalData.setComplexColumnLabel(nColSize-1, std::move(aLabels));
+ }
+
+ OUString aRangeRep = lcl_aLabelRangePrefix + OUString::number(nColSize-1);
+ xSeq.set(new UncachedDataSequence(this, aRangeRep));
+ addDataSequenceToMap(aRangeRep, xSeq);
+ }
+ }
+
+ return xSeq;
+}
+
+Reference< chart2::data::XDataSequence > InternalDataProvider::createDataSequenceAndAddToMap(
+ const OUString & rRangeRepresentation,
+ const OUString & rRole )
+{
+ rtl::Reference< UncachedDataSequence > xSeq =
+ new UncachedDataSequence( this, rRangeRepresentation, rRole );
+ addDataSequenceToMap( rRangeRepresentation, xSeq );
+ return xSeq;
+}
+
+// ____ XDataProvider ____
+sal_Bool SAL_CALL InternalDataProvider::createDataSourcePossible( const Sequence< beans::PropertyValue >& /* aArguments */ )
+{
+ return true;
+}
+
+namespace
+{
+
+sal_Int32 lcl_getInnerLevelCount( const vector< vector< uno::Any > >& rLabels )
+{
+ sal_Int32 nCount = 1;//minimum is 1!
+ for (auto const& elemLabel : rLabels)
+ {
+ nCount = std::max<sal_Int32>( elemLabel.size(), nCount );
+ }
+ return nCount;
+}
+
+}//end anonymous namespace
+
+Reference< chart2::data::XDataSource > SAL_CALL InternalDataProvider::createDataSource(
+ const Sequence< beans::PropertyValue >& aArguments )
+{
+ OUString aRangeRepresentation;
+ bool bUseColumns = true;
+ bool bFirstCellAsLabel = true;
+ bool bHasCategories = true;
+ uno::Sequence< sal_Int32 > aSequenceMapping;
+ DataSourceHelper::readArguments( aArguments, aRangeRepresentation, aSequenceMapping, bUseColumns, bFirstCellAsLabel, bHasCategories );
+
+ if( aRangeRepresentation == lcl_aCategoriesRangeName )
+ {
+ //return split complex categories if we have any:
+ std::vector< Reference< chart2::data::XLabeledDataSequence > > aComplexCategories;
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ if( bUseColumns==m_bDataInColumns )
+ {
+ sal_Int32 nLevelCount = lcl_getInnerLevelCount( aCategories );
+ for( sal_Int32 nL=0; nL<nLevelCount; nL++ )
+ aComplexCategories.push_back( new LabeledDataSequence(
+ new UncachedDataSequence( this
+ , lcl_aCategoriesLevelRangeNamePrefix + OUString::number( nL )
+ , lcl_aCategoriesRoleName ) ) );
+ }
+ else
+ {
+ sal_Int32 nPointCount = m_bDataInColumns ? m_aInternalData.getRowCount() : m_aInternalData.getColumnCount();
+ for( sal_Int32 nP=0; nP<nPointCount; nP++ )
+ aComplexCategories.push_back( new LabeledDataSequence(
+ new UncachedDataSequence( this
+ , lcl_aCategoriesPointRangeNamePrefix + OUString::number( nP )
+ , lcl_aCategoriesRoleName ) ) );
+ }
+ //don't add the created sequences to the map as they are used temporarily only ...
+ return new DataSource( comphelper::containerToSequence(aComplexCategories) );
+ }
+
+ OSL_ASSERT( aRangeRepresentation == lcl_aCompleteRange );
+
+ std::vector< Reference< chart2::data::XLabeledDataSequence > > aResultLSeqVec;
+
+ // categories
+ if( bHasCategories )
+ aResultLSeqVec.push_back(
+ new LabeledDataSequence( createDataSequenceAndAddToMap( lcl_aCategoriesRangeName, lcl_aCategoriesRoleName ) ) );
+
+ // data with labels
+ std::vector< Reference< chart2::data::XLabeledDataSequence > > aDataVec;
+ const sal_Int32 nCount = (bUseColumns ? m_aInternalData.getColumnCount() : m_aInternalData.getRowCount());
+ aDataVec.reserve(nCount);
+ for (sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx)
+ {
+ aDataVec.push_back(
+ new LabeledDataSequence(
+ createDataSequenceAndAddToMap( OUString::number( nIdx )),
+ createDataSequenceAndAddToMap( lcl_aLabelRangePrefix + OUString::number( nIdx ))));
+ }
+
+ // attention: this data provider has the limitation that it stores
+ // internally if data comes from columns or rows. It is intended for
+ // creating only one used data source.
+ // @todo: add this information in the range representation strings
+ m_bDataInColumns = bUseColumns;
+
+ //reorder labeled sequences according to aSequenceMapping; ignore categories
+ for( sal_Int32 nNewIndex = 0; nNewIndex < aSequenceMapping.getLength(); nNewIndex++ )
+ {
+ std::vector< LabeledDataSequence* >::size_type nOldIndex = aSequenceMapping[nNewIndex];
+ if( nOldIndex < aDataVec.size() )
+ {
+ if( aDataVec[nOldIndex].is() )
+ {
+ aResultLSeqVec.push_back( aDataVec[nOldIndex] );
+ aDataVec[nOldIndex] = nullptr;
+ }
+ }
+ }
+
+ //add left over data sequences to result
+ for (auto const& elem : aDataVec)
+ {
+ if( elem.is() )
+ aResultLSeqVec.push_back(elem);
+ }
+
+ return new DataSource( comphelper::containerToSequence(aResultLSeqVec) );
+}
+
+Sequence< beans::PropertyValue > SAL_CALL InternalDataProvider::detectArguments(
+ const Reference< chart2::data::XDataSource >& /* xDataSource */ )
+{
+ Sequence< beans::PropertyValue > aArguments{
+ beans::PropertyValue(
+ "CellRangeRepresentation", -1, uno::Any( OUString(lcl_aCompleteRange) ),
+ beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "DataRowSource", -1, uno::Any(
+ m_bDataInColumns
+ ? css::chart::ChartDataRowSource_COLUMNS
+ : css::chart::ChartDataRowSource_ROWS ),
+ beans::PropertyState_DIRECT_VALUE ),
+ // internal data always contains labels and categories
+ beans::PropertyValue(
+ "FirstCellAsLabel", -1, uno::Any( true ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "HasCategories", -1, uno::Any( true ), beans::PropertyState_DIRECT_VALUE )
+ };
+ // #i85913# Sequence Mapping is not needed for internal data, as it is
+ // applied to the data when the data source is created.
+
+ return aArguments;
+}
+
+sal_Bool SAL_CALL InternalDataProvider::createDataSequenceByRangeRepresentationPossible( const OUString& /* aRangeRepresentation */ )
+{
+ return true;
+}
+
+Reference< chart2::data::XDataSequence > SAL_CALL InternalDataProvider::createDataSequenceByRangeRepresentation(
+ const OUString& aRangeRepresentation )
+{
+ if( aRangeRepresentation.match( lcl_aCategoriesRangeName ))
+ {
+ OSL_ASSERT( aRangeRepresentation == lcl_aCategoriesRangeName );//it is not expected nor implemented that only parts of the categories are really requested
+
+ // categories
+ return createDataSequenceAndAddToMap( lcl_aCategoriesRangeName, lcl_aCategoriesRoleName );
+ }
+ else if( aRangeRepresentation.match( lcl_aLabelRangePrefix ))
+ {
+ // label
+ sal_Int32 nIndex = o3tl::toInt32(aRangeRepresentation.subView( strlen(lcl_aLabelRangePrefix)));
+ return createDataSequenceAndAddToMap( lcl_aLabelRangePrefix + OUString::number( nIndex ));
+ }
+ else if ( aRangeRepresentation == "last" )
+ {
+ sal_Int32 nIndex = (m_bDataInColumns
+ ? m_aInternalData.getColumnCount()
+ : m_aInternalData.getRowCount()) - 1;
+ return createDataSequenceAndAddToMap( OUString::number( nIndex ));
+ }
+ else if( !aRangeRepresentation.isEmpty())
+ {
+ // data
+ return createDataSequenceAndAddToMap( aRangeRepresentation );
+ }
+
+ return Reference< chart2::data::XDataSequence >();
+}
+
+Reference<chart2::data::XDataSequence> SAL_CALL
+InternalDataProvider::createDataSequenceByValueArray(
+ const OUString& aRole, const OUString& aRangeRepresentation, const OUString& aRoleQualifier )
+{
+ return createDataSequenceFromArray(aRangeRepresentation, aRole, aRoleQualifier);
+}
+
+Reference< sheet::XRangeSelection > SAL_CALL InternalDataProvider::getRangeSelection()
+{
+ // there is no range selection component
+ return Reference< sheet::XRangeSelection >();
+}
+
+// ____ XInternalDataProvider ____
+sal_Bool SAL_CALL InternalDataProvider::hasDataByRangeRepresentation( const OUString& aRange )
+{
+ bool bResult = false;
+
+ if( aRange.match( lcl_aCategoriesRangeName ))
+ {
+ OSL_ASSERT( aRange == lcl_aCategoriesRangeName );//it is not expected nor implemented that only parts of the categories are really requested
+ bResult = true;
+ }
+ else if( aRange.match( lcl_aLabelRangePrefix ))
+ {
+ sal_Int32 nIndex = o3tl::toInt32(aRange.subView( strlen(lcl_aLabelRangePrefix)));
+ bResult = (nIndex < (m_bDataInColumns ? m_aInternalData.getColumnCount(): m_aInternalData.getRowCount()));
+ }
+ else
+ {
+ sal_Int32 nIndex = aRange.toInt32();
+ bResult = (nIndex < (m_bDataInColumns ? m_aInternalData.getColumnCount(): m_aInternalData.getRowCount()));
+ }
+
+ return bResult;
+}
+
+Sequence< uno::Any > SAL_CALL InternalDataProvider::getDataByRangeRepresentation( const OUString& aRange )
+{
+ Sequence< uno::Any > aResult;
+
+ if( aRange.match( lcl_aLabelRangePrefix ) )
+ {
+ auto nIndex = o3tl::toUInt32(aRange.subView( strlen(lcl_aLabelRangePrefix)));
+ vector< uno::Any > aComplexLabel = m_bDataInColumns
+ ? m_aInternalData.getComplexColumnLabel( nIndex )
+ : m_aInternalData.getComplexRowLabel( nIndex );
+ if( !aComplexLabel.empty() )
+ aResult = comphelper::containerToSequence(aComplexLabel);
+ }
+ else if( aRange.match( lcl_aCategoriesPointRangeNamePrefix ) )
+ {
+ auto nPointIndex = o3tl::toUInt32(aRange.subView( strlen(lcl_aCategoriesPointRangeNamePrefix) ));
+ vector< uno::Any > aComplexCategory = m_bDataInColumns
+ ? m_aInternalData.getComplexRowLabel( nPointIndex )
+ : m_aInternalData.getComplexColumnLabel( nPointIndex );
+ if( !aComplexCategory.empty() )
+ aResult = comphelper::containerToSequence(aComplexCategory);
+ }
+ else if( aRange.match( lcl_aCategoriesLevelRangeNamePrefix ) )
+ {
+ sal_Int32 nLevel = o3tl::toInt32(aRange.subView( strlen(lcl_aCategoriesLevelRangeNamePrefix) ));
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ if( nLevel < lcl_getInnerLevelCount( aCategories ) )
+ {
+ aResult.realloc( aCategories.size() );
+ transform( aCategories.begin(), aCategories.end(),
+ aResult.getArray(), lcl_copyFromLevel(nLevel) );
+ }
+ }
+ else if( aRange == lcl_aCategoriesRangeName )
+ {
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ sal_Int32 nLevelCount = lcl_getInnerLevelCount( aCategories );
+ if( nLevelCount == 1 )
+ {
+ aResult = getDataByRangeRepresentation( lcl_aCategoriesLevelRangeNamePrefix + OUString::number( 0 ) );
+ }
+ else
+ {
+ // Maybe this 'else' part and the functions is not necessary anymore.
+ const Sequence< OUString > aLabels = m_bDataInColumns ? getRowDescriptions() : getColumnDescriptions();
+ aResult.realloc( aLabels.getLength() );
+ transform( aLabels.begin(), aLabels.end(),
+ aResult.getArray(), CommonFunctors::makeAny< OUString >() );
+ }
+ }
+ else
+ {
+ sal_Int32 nIndex = aRange.toInt32();
+ if( nIndex >= 0 )
+ {
+ const Sequence< double > aData = m_bDataInColumns
+ ? m_aInternalData.getColumnValues(nIndex)
+ : m_aInternalData.getRowValues(nIndex);
+ if( aData.hasElements() )
+ {
+ aResult.realloc( aData.getLength());
+ transform( aData.begin(), aData.end(),
+ aResult.getArray(), CommonFunctors::makeAny< double >());
+ }
+ }
+ }
+
+ return aResult;
+}
+
+void SAL_CALL InternalDataProvider::setDataByRangeRepresentation(
+ const OUString& aRange, const Sequence< uno::Any >& aNewData )
+{
+ auto aNewVector( comphelper::sequenceToContainer<vector< uno::Any >>(aNewData) );
+ if( aRange.match( lcl_aLabelRangePrefix ) )
+ {
+ sal_uInt32 nIndex = o3tl::toInt32(aRange.subView( strlen(lcl_aLabelRangePrefix)));
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexColumnLabel( nIndex, std::move(aNewVector) );
+ else
+ m_aInternalData.setComplexRowLabel( nIndex, std::move(aNewVector) );
+ }
+ else if( aRange.match( lcl_aCategoriesPointRangeNamePrefix ) )
+ {
+ sal_Int32 nPointIndex = o3tl::toInt32(aRange.subView( strlen(lcl_aCategoriesLevelRangeNamePrefix)));
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabel( nPointIndex, std::move(aNewVector) );
+ else
+ m_aInternalData.setComplexColumnLabel( nPointIndex, std::move(aNewVector) );
+ }
+ else if( aRange.match( lcl_aCategoriesLevelRangeNamePrefix ) )
+ {
+ sal_Int32 nLevel = o3tl::toInt32(aRange.subView( strlen(lcl_aCategoriesLevelRangeNamePrefix)));
+ vector< vector< uno::Any > > aComplexCategories = m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels();
+
+ //ensure equal length
+ if( aNewVector.size() > aComplexCategories.size() )
+ aComplexCategories.resize( aNewVector.size() );
+ else if( aNewVector.size() < aComplexCategories.size() )
+ aNewVector.resize( aComplexCategories.size() );
+
+ transform( aComplexCategories.begin(), aComplexCategories.end(), aNewVector.begin(),
+ aComplexCategories.begin(), lcl_setAnyAtLevel(nLevel) );
+
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+ }
+ else if( aRange == lcl_aCategoriesRangeName )
+ {
+ vector< vector< uno::Any > > aComplexCategories;
+ aComplexCategories.resize( aNewVector.size() );
+ transform( aComplexCategories.begin(), aComplexCategories.end(), aNewVector.begin(),
+ aComplexCategories.begin(), lcl_setAnyAtLevel(0) );
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+ }
+ else
+ {
+ sal_Int32 nIndex = aRange.toInt32();
+ if( nIndex>=0 )
+ {
+ vector< double > aNewDataVec;
+ transform( aNewData.begin(), aNewData.end(),
+ back_inserter( aNewDataVec ), CommonFunctors::AnyToDouble());
+ if( m_bDataInColumns )
+ m_aInternalData.setColumnValues( nIndex, aNewDataVec );
+ else
+ m_aInternalData.setRowValues( nIndex, aNewDataVec );
+ }
+ }
+}
+
+void SAL_CALL InternalDataProvider::insertSequence( ::sal_Int32 nAfterIndex )
+{
+ if( m_bDataInColumns )
+ {
+ increaseMapReferences( nAfterIndex + 1, m_aInternalData.getColumnCount());
+ m_aInternalData.insertColumn( nAfterIndex );
+ }
+ else
+ {
+ increaseMapReferences( nAfterIndex + 1, m_aInternalData.getRowCount());
+ m_aInternalData.insertRow( nAfterIndex );
+ }
+}
+
+void SAL_CALL InternalDataProvider::deleteSequence( ::sal_Int32 nAtIndex )
+{
+ deleteMapReferences( OUString::number( nAtIndex ));
+ deleteMapReferences( lcl_aLabelRangePrefix + OUString::number( nAtIndex ));
+ if( m_bDataInColumns )
+ {
+ decreaseMapReferences( nAtIndex + 1, m_aInternalData.getColumnCount());
+ m_aInternalData.deleteColumn( nAtIndex );
+ }
+ else
+ {
+ decreaseMapReferences( nAtIndex + 1, m_aInternalData.getRowCount());
+ m_aInternalData.deleteRow( nAtIndex );
+ }
+}
+
+void SAL_CALL InternalDataProvider::appendSequence()
+{
+ if( m_bDataInColumns )
+ m_aInternalData.appendColumn();
+ else
+ m_aInternalData.appendRow();
+}
+
+void SAL_CALL InternalDataProvider::insertComplexCategoryLevel( sal_Int32 nLevel )
+{
+ OSL_ENSURE( nLevel> 0, "you can only insert category levels > 0" );//the first categories level cannot be deleted, check the calling code for error
+ if( nLevel>0 )
+ {
+ vector< vector< uno::Any > > aComplexCategories = m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels();
+ std::for_each( aComplexCategories.begin(), aComplexCategories.end(), lcl_insertAnyAtLevel(nLevel) );
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+ }
+}
+void SAL_CALL InternalDataProvider::deleteComplexCategoryLevel( sal_Int32 nLevel )
+{
+ OSL_ENSURE( nLevel>0, "you can only delete category levels > 0" );//the first categories level cannot be deleted, check the calling code for error
+ if( nLevel>0 )
+ {
+ vector< vector< uno::Any > > aComplexCategories = m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels();
+ std::for_each( aComplexCategories.begin(), aComplexCategories.end(), lcl_removeAnyAtLevel(nLevel) );
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+ }
+}
+
+void SAL_CALL InternalDataProvider::insertDataPointForAllSequences( ::sal_Int32 nAfterIndex )
+{
+ sal_Int32 nMaxRep = 0;
+ if( m_bDataInColumns )
+ {
+ m_aInternalData.insertRow( nAfterIndex );
+ nMaxRep = m_aInternalData.getColumnCount();
+ }
+ else
+ {
+ m_aInternalData.insertColumn( nAfterIndex );
+ nMaxRep = m_aInternalData.getRowCount();
+ }
+
+ // notify change to all affected ranges
+ tSequenceMap::const_iterator aBegin( m_aSequenceMap.lower_bound( "0"));
+ tSequenceMap::const_iterator aEnd( m_aSequenceMap.upper_bound( OUString::number( nMaxRep )));
+ std::for_each( aBegin, aEnd, lcl_setModified());
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+}
+
+void SAL_CALL InternalDataProvider::deleteDataPointForAllSequences( ::sal_Int32 nAtIndex )
+{
+ sal_Int32 nMaxRep = 0;
+ if( m_bDataInColumns )
+ {
+ m_aInternalData.deleteRow( nAtIndex );
+ nMaxRep = m_aInternalData.getColumnCount();
+ }
+ else
+ {
+ m_aInternalData.deleteColumn( nAtIndex );
+ nMaxRep = m_aInternalData.getRowCount();
+ }
+
+ // notify change to all affected ranges
+ tSequenceMap::const_iterator aBegin( m_aSequenceMap.lower_bound( "0"));
+ tSequenceMap::const_iterator aEnd( m_aSequenceMap.upper_bound( OUString::number( nMaxRep )));
+ std::for_each( aBegin, aEnd, lcl_setModified());
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+}
+
+void SAL_CALL InternalDataProvider::swapDataPointWithNextOneForAllSequences( ::sal_Int32 nAtIndex )
+{
+ if( m_bDataInColumns )
+ m_aInternalData.swapRowWithNext( nAtIndex );
+ else
+ m_aInternalData.swapColumnWithNext( nAtIndex );
+ sal_Int32 nMaxRep = (m_bDataInColumns
+ ? m_aInternalData.getColumnCount()
+ : m_aInternalData.getRowCount());
+
+ // notify change to all affected ranges
+ tSequenceMap::const_iterator aBegin( m_aSequenceMap.lower_bound( "0"));
+ tSequenceMap::const_iterator aEnd( m_aSequenceMap.upper_bound( OUString::number( nMaxRep )));
+ std::for_each( aBegin, aEnd, lcl_setModified());
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+}
+
+void SAL_CALL InternalDataProvider::registerDataSequenceForChanges( const Reference< chart2::data::XDataSequence >& xSeq )
+{
+ if( xSeq.is())
+ addDataSequenceToMap( xSeq->getSourceRangeRepresentation(), xSeq );
+}
+
+// ____ XRangeXMLConversion ____
+OUString SAL_CALL InternalDataProvider::convertRangeToXML( const OUString& aRangeRepresentation )
+{
+ XMLRangeHelper::CellRange aRange;
+ aRange.aTableName = "local-table";
+
+ // attention: this data provider has the limitation that it stores
+ // internally if data comes from columns or rows. It is intended for
+ // creating only one used data source.
+ // @todo: add this information in the range representation strings
+ if( aRangeRepresentation.match( lcl_aCategoriesRangeName ))
+ {
+ OSL_ASSERT( aRangeRepresentation == lcl_aCategoriesRangeName );//it is not expected nor implemented that only parts of the categories are really requested
+ aRange.aUpperLeft.bIsEmpty = false;
+ if( m_bDataInColumns )
+ {
+ aRange.aUpperLeft.nColumn = 0;
+ aRange.aUpperLeft.nRow = 1;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nRow = m_aInternalData.getRowCount();
+ }
+ else
+ {
+ aRange.aUpperLeft.nColumn = 1;
+ aRange.aUpperLeft.nRow = 0;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nColumn = m_aInternalData.getColumnCount();
+ }
+ }
+ else if( aRangeRepresentation.match( lcl_aLabelRangePrefix ))
+ {
+ sal_Int32 nIndex = o3tl::toInt32(aRangeRepresentation.subView( strlen(lcl_aLabelRangePrefix)));
+ aRange.aUpperLeft.bIsEmpty = false;
+ aRange.aLowerRight.bIsEmpty = true;
+ if( m_bDataInColumns )
+ {
+ aRange.aUpperLeft.nColumn = nIndex + 1;
+ aRange.aUpperLeft.nRow = 0;
+ }
+ else
+ {
+ aRange.aUpperLeft.nColumn = 0;
+ aRange.aUpperLeft.nRow = nIndex + 1;
+ }
+ }
+ else if( aRangeRepresentation == lcl_aCompleteRange )
+ {
+ aRange.aUpperLeft.bIsEmpty = false;
+ aRange.aLowerRight.bIsEmpty = false;
+ aRange.aUpperLeft.nColumn = 0;
+ aRange.aUpperLeft.nRow = 0;
+ aRange.aLowerRight.nColumn = m_aInternalData.getColumnCount();
+ aRange.aLowerRight.nRow = m_aInternalData.getRowCount();
+ }
+ else
+ {
+ sal_Int32 nIndex = aRangeRepresentation.toInt32();
+ aRange.aUpperLeft.bIsEmpty = false;
+ if( m_bDataInColumns )
+ {
+ aRange.aUpperLeft.nColumn = nIndex + 1;
+ aRange.aUpperLeft.nRow = 1;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nRow = m_aInternalData.getRowCount();
+ }
+ else
+ {
+ aRange.aUpperLeft.nColumn = 1;
+ aRange.aUpperLeft.nRow = nIndex + 1;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nColumn = m_aInternalData.getColumnCount();
+ }
+ }
+
+ return XMLRangeHelper::getXMLStringFromCellRange( aRange );
+}
+
+OUString SAL_CALL InternalDataProvider::convertRangeFromXML( const OUString& aXMLRange )
+{
+ // Handle non-standards-conforming table:cell-range-address="PivotChart", see
+ // <https://bugs.documentfoundation.org/show_bug.cgi?id=112783> "PIVOT CHARTS: Save produces
+ // invalid file because of invalid cell address":
+ if (aXMLRange == "PivotChart") {
+ return "";
+ }
+
+ static const OUStringLiteral aPivotTableID(u"PT@");
+ if (aXMLRange.startsWith(aPivotTableID))
+ return aXMLRange.copy(aPivotTableID.getLength());
+
+ XMLRangeHelper::CellRange aRange( XMLRangeHelper::getCellRangeFromXMLString( aXMLRange ));
+ if( aRange.aUpperLeft.bIsEmpty )
+ {
+ OSL_ENSURE( aRange.aLowerRight.bIsEmpty, "Weird Range" );
+ return OUString();
+ }
+
+ // "all"
+ if( !aRange.aLowerRight.bIsEmpty &&
+ ( aRange.aUpperLeft.nColumn != aRange.aLowerRight.nColumn ) &&
+ ( aRange.aUpperLeft.nRow != aRange.aLowerRight.nRow ) )
+ return lcl_aCompleteRange;
+
+ // attention: this data provider has the limitation that it stores
+ // internally if data comes from columns or rows. It is intended for
+ // creating only one used data source.
+ // @todo: add this information in the range representation strings
+
+ // data in columns
+ if( m_bDataInColumns )
+ {
+ if( aRange.aUpperLeft.nColumn == 0 )
+ return lcl_aCategoriesRangeName;
+ if( aRange.aUpperLeft.nRow == 0 )
+ return lcl_aLabelRangePrefix + OUString::number( aRange.aUpperLeft.nColumn - 1 );
+
+ return OUString::number( aRange.aUpperLeft.nColumn - 1 );
+ }
+
+ // data in rows
+ if( aRange.aUpperLeft.nRow == 0 )
+ return lcl_aCategoriesRangeName;
+ if( aRange.aUpperLeft.nColumn == 0 )
+ return lcl_aLabelRangePrefix + OUString::number( aRange.aUpperLeft.nRow - 1 );
+
+ return OUString::number( aRange.aUpperLeft.nRow - 1 );
+}
+
+namespace
+{
+
+template< class Type >
+Sequence< Sequence< Type > > lcl_convertVectorVectorToSequenceSequence( const vector< vector< Type > >& rIn )
+{
+ Sequence< Sequence< Type > > aRet;
+ sal_Int32 nOuterCount = rIn.size();
+ if( nOuterCount )
+ {
+ aRet.realloc(nOuterCount);
+ auto pRet = aRet.getArray();
+ for( sal_Int32 nN=0; nN<nOuterCount; nN++)
+ pRet[nN]= comphelper::containerToSequence( rIn[nN] );
+ }
+ return aRet;
+}
+
+template< class Type >
+vector< vector< Type > > lcl_convertSequenceSequenceToVectorVector( const Sequence< Sequence< Type > >& rIn )
+{
+ vector< vector< Type > > aRet;
+ sal_Int32 nOuterCount = rIn.getLength();
+ if( nOuterCount )
+ {
+ aRet.resize(nOuterCount);
+ for( sal_Int32 nN=0; nN<nOuterCount; nN++)
+ aRet[nN]= comphelper::sequenceToContainer<vector< Type >>( rIn[nN] );
+ }
+ return aRet;
+}
+
+std::vector< Sequence< OUString > > lcl_convertComplexAnyVectorToStringSequence( const vector< vector< uno::Any > >& rIn )
+{
+ std::vector< Sequence< OUString > > aRet;
+ sal_Int32 nOuterCount = rIn.size();
+ if( nOuterCount )
+ {
+ aRet.resize(nOuterCount);
+ for( sal_Int32 nN=0; nN<nOuterCount; nN++)
+ aRet[nN] = comphelper::containerToSequence(lcl_AnyToStringSequence( rIn[nN] ));
+ }
+ return aRet;
+}
+
+vector< vector< uno::Any > > lcl_convertComplexStringSequenceToAnyVector( const Sequence< Sequence< OUString > >& rIn )
+{
+ vector< vector< uno::Any > > aRet;
+ sal_Int32 nOuterCount = rIn.getLength();
+ aRet.reserve(nOuterCount);
+ for (sal_Int32 nN = 0; nN < nOuterCount; nN++)
+ aRet.push_back( lcl_StringToAnyVector( rIn[nN] ) );
+ return aRet;
+}
+
+class SplitCategoriesProvider_ForComplexDescriptions : public SplitCategoriesProvider
+{
+public:
+
+ explicit SplitCategoriesProvider_ForComplexDescriptions( const std::vector< std::vector< uno::Any > >& rComplexDescriptions )
+ : m_rComplexDescriptions( rComplexDescriptions )
+ {}
+
+ virtual sal_Int32 getLevelCount() const override;
+ virtual uno::Sequence< OUString > getStringsForLevel( sal_Int32 nIndex ) const override;
+
+private:
+ const std::vector< std::vector< uno::Any > >& m_rComplexDescriptions;
+};
+
+sal_Int32 SplitCategoriesProvider_ForComplexDescriptions::getLevelCount() const
+{
+ return lcl_getInnerLevelCount( m_rComplexDescriptions );
+}
+uno::Sequence< OUString > SplitCategoriesProvider_ForComplexDescriptions::getStringsForLevel( sal_Int32 nLevel ) const
+{
+ uno::Sequence< OUString > aResult;
+ if( nLevel < lcl_getInnerLevelCount( m_rComplexDescriptions ) )
+ {
+ aResult.realloc( m_rComplexDescriptions.size() );
+ transform( m_rComplexDescriptions.begin(), m_rComplexDescriptions.end(),
+ aResult.getArray(), lcl_getStringFromLevelVector(nLevel) );
+ }
+ return aResult;
+}
+
+}//anonymous namespace
+
+// ____ XDateCategories ____
+Sequence< double > SAL_CALL InternalDataProvider::getDateCategories()
+{
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ sal_Int32 nCount = aCategories.size();
+ Sequence< double > aDoubles( nCount );
+ auto aDoublesRange = asNonConstRange(aDoubles);
+ sal_Int32 nN=0;
+ for (auto const& category : aCategories)
+ {
+ double fValue;
+ if( category.empty() || !(category[0]>>=fValue) )
+ fValue = std::numeric_limits<double>::quiet_NaN();
+ aDoublesRange[nN++]=fValue;
+ }
+ return aDoubles;
+}
+
+void SAL_CALL InternalDataProvider::setDateCategories( const Sequence< double >& rDates )
+{
+ sal_Int32 nCount = rDates.getLength();
+ vector< vector< uno::Any > > aNewCategories;
+ aNewCategories.reserve(nCount);
+ vector< uno::Any > aSingleLabel(1);
+
+ for(sal_Int32 nN=0; nN<nCount; ++nN )
+ {
+ aSingleLabel[0] <<= rDates[nN];
+ aNewCategories.push_back(aSingleLabel);
+ }
+
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aNewCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aNewCategories) );
+}
+
+// ____ XAnyDescriptionAccess ____
+Sequence< Sequence< uno::Any > > SAL_CALL InternalDataProvider::getAnyRowDescriptions()
+{
+ return lcl_convertVectorVectorToSequenceSequence( m_aInternalData.getComplexRowLabels() );
+}
+void SAL_CALL InternalDataProvider::setAnyRowDescriptions( const Sequence< Sequence< uno::Any > >& aRowDescriptions )
+{
+ m_aInternalData.setComplexRowLabels( lcl_convertSequenceSequenceToVectorVector( aRowDescriptions ) );
+}
+Sequence< Sequence< uno::Any > > SAL_CALL InternalDataProvider::getAnyColumnDescriptions()
+{
+ return lcl_convertVectorVectorToSequenceSequence( m_aInternalData.getComplexColumnLabels() );
+}
+void SAL_CALL InternalDataProvider::setAnyColumnDescriptions( const Sequence< Sequence< uno::Any > >& aColumnDescriptions )
+{
+ m_aInternalData.setComplexColumnLabels( lcl_convertSequenceSequenceToVectorVector( aColumnDescriptions ) );
+}
+
+// ____ XComplexDescriptionAccess ____
+Sequence< Sequence< OUString > > SAL_CALL InternalDataProvider::getComplexRowDescriptions()
+{
+ return comphelper::containerToSequence(lcl_convertComplexAnyVectorToStringSequence( m_aInternalData.getComplexRowLabels() ));
+}
+void SAL_CALL InternalDataProvider::setComplexRowDescriptions( const Sequence< Sequence< OUString > >& aRowDescriptions )
+{
+ m_aInternalData.setComplexRowLabels( lcl_convertComplexStringSequenceToAnyVector(aRowDescriptions) );
+}
+Sequence< Sequence< OUString > > SAL_CALL InternalDataProvider::getComplexColumnDescriptions()
+{
+ return comphelper::containerToSequence(lcl_convertComplexAnyVectorToStringSequence( m_aInternalData.getComplexColumnLabels() ));
+}
+void SAL_CALL InternalDataProvider::setComplexColumnDescriptions( const Sequence< Sequence< OUString > >& aColumnDescriptions )
+{
+ m_aInternalData.setComplexColumnLabels( lcl_convertComplexStringSequenceToAnyVector(aColumnDescriptions) );
+}
+
+// ____ XChartDataArray ____
+Sequence< Sequence< double > > SAL_CALL InternalDataProvider::getData()
+{
+ return m_aInternalData.getData();
+}
+
+void SAL_CALL InternalDataProvider::setData( const Sequence< Sequence< double > >& rDataInRows )
+{
+ return m_aInternalData.setData( rDataInRows );
+}
+
+void SAL_CALL InternalDataProvider::setRowDescriptions( const Sequence< OUString >& aRowDescriptions )
+{
+ vector< vector< uno::Any > > aComplexDescriptions( aRowDescriptions.getLength() );
+ transform( aComplexDescriptions.begin(), aComplexDescriptions.end(), aRowDescriptions.getConstArray(),
+ aComplexDescriptions.begin(), lcl_setAnyAtLevelFromStringSequence(0) );
+ m_aInternalData.setComplexRowLabels( std::move(aComplexDescriptions) );
+}
+
+void SAL_CALL InternalDataProvider::setColumnDescriptions( const Sequence< OUString >& aColumnDescriptions )
+{
+ vector< vector< uno::Any > > aComplexDescriptions( aColumnDescriptions.getLength() );
+ transform( aComplexDescriptions.begin(), aComplexDescriptions.end(), aColumnDescriptions.getConstArray(),
+ aComplexDescriptions.begin(), lcl_setAnyAtLevelFromStringSequence(0) );
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexDescriptions) );
+}
+
+Sequence< OUString > SAL_CALL InternalDataProvider::getRowDescriptions()
+{
+ const vector< vector< uno::Any > > & aComplexLabels( m_aInternalData.getComplexRowLabels() );
+ SplitCategoriesProvider_ForComplexDescriptions aProvider( aComplexLabels );
+ return ExplicitCategoriesProvider::getExplicitSimpleCategories( aProvider );
+}
+
+Sequence< OUString > SAL_CALL InternalDataProvider::getColumnDescriptions()
+{
+ const vector< vector< uno::Any > > & aComplexLabels( m_aInternalData.getComplexColumnLabels() );
+ SplitCategoriesProvider_ForComplexDescriptions aProvider( aComplexLabels );
+ return ExplicitCategoriesProvider::getExplicitSimpleCategories( aProvider );
+}
+
+// ____ XChartData (base of XChartDataArray) ____
+void SAL_CALL InternalDataProvider::addChartDataChangeEventListener(
+ const Reference< css::chart::XChartDataChangeEventListener >& )
+{
+}
+
+void SAL_CALL InternalDataProvider::removeChartDataChangeEventListener(
+ const Reference< css::chart::XChartDataChangeEventListener >& )
+{
+}
+
+double SAL_CALL InternalDataProvider::getNotANumber()
+{
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+sal_Bool SAL_CALL InternalDataProvider::isNotANumber( double nNumber )
+{
+ return std::isnan( nNumber )
+ || std::isinf( nNumber );
+}
+// lang::XInitialization:
+void SAL_CALL InternalDataProvider::initialize(const uno::Sequence< uno::Any > & _aArguments)
+{
+ comphelper::SequenceAsHashMap aArgs(_aArguments);
+ if ( aArgs.getUnpackedValueOrDefault( "CreateDefaultData", false ) )
+ m_aInternalData.createDefaultData();
+}
+
+// ____ XCloneable ____
+Reference< util::XCloneable > SAL_CALL InternalDataProvider::createClone()
+{
+ return Reference< util::XCloneable >( new InternalDataProvider( *this ));
+}
+
+OUString SAL_CALL InternalDataProvider::getImplementationName()
+{
+ // note: in xmloff this name is used to indicate usage of own data
+ return "com.sun.star.comp.chart.InternalDataProvider";
+}
+
+sal_Bool SAL_CALL InternalDataProvider::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL InternalDataProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.data.DataProvider" };
+}
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart_InternalDataProvider_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::InternalDataProvider);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */