LibreOffice Module chart2 (master) 1
ExplicitCategoriesProvider.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
21#include <DiagramHelper.hxx>
22#include <ChartType.hxx>
23#include <ChartTypeHelper.hxx>
24#include <Axis.hxx>
25#include <AxisHelper.hxx>
26#include <DataSourceHelper.hxx>
27#include <ChartModel.hxx>
28#include <ChartModelHelper.hxx>
30#include <unonames.hxx>
32#include <DataSeries.hxx>
33
34#include <com/sun/star/chart2/AxisType.hpp>
35#include <o3tl/safeint.hxx>
36#include <rtl/ustrbuf.hxx>
38
39#include <limits>
40
41namespace chart
42{
43
44using namespace ::com::sun::star;
45using namespace ::com::sun::star::chart2;
46using ::com::sun::star::uno::Reference;
47using ::com::sun::star::uno::Sequence;
48using std::vector;
49
51 , ChartModel& rModel )
52 : m_bDirty(true)
53 , m_xCooSysModel( xCooSysModel.get() )
54 , mrModel(rModel)
55 , m_bIsExplicitCategoriesInited(false)
56 , m_bIsDateAxis(false)
57 , m_bIsAutoDate(false)
58{
59 try
60 {
61 if( xCooSysModel.is() )
62 {
63 // TODO: handle different category names on the primary and secondary category axis.
64 rtl::Reference< Axis > xAxis = xCooSysModel->getAxisByDimension2(0,0);
65 if( xAxis.is() )
66 {
67 ScaleData aScale( xAxis->getScaleData() );
68 m_xOriginalCategories = aScale.Categories;
69 m_bIsAutoDate = (aScale.AutoDateAxis && aScale.AxisType==chart2::AxisType::CATEGORY);
70 m_bIsDateAxis = (aScale.AxisType == chart2::AxisType::DATE || m_bIsAutoDate);
71 }
72 }
73
74 if( m_xOriginalCategories.is() )
75 {
76 uno::Reference< data::XDataProvider > xDataProvider( mrModel.getDataProvider() );
77
78 OUString aCategoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories ) );
79 if( xDataProvider.is() && !aCategoriesRange.isEmpty() )
80 {
81 const bool bFirstCellAsLabel = false;
82 const bool bHasCategories = false;
83 const uno::Sequence< sal_Int32 > aSequenceMapping;
84
85 uno::Reference< data::XDataSource > xColumnCategoriesSource( xDataProvider->createDataSource(
86 DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, true /*bUseColumns*/
87 , bFirstCellAsLabel, bHasCategories ) ) );
88
89 uno::Reference< data::XDataSource > xRowCategoriesSource( xDataProvider->createDataSource(
90 DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, false /*bUseColumns*/
91 , bFirstCellAsLabel, bHasCategories ) ) );
92
93 if( xColumnCategoriesSource.is() && xRowCategoriesSource.is() )
94 {
95 Sequence< Reference< data::XLabeledDataSequence> > aColumns = xColumnCategoriesSource->getDataSequences();
96 Sequence< Reference< data::XLabeledDataSequence> > aRows = xRowCategoriesSource->getDataSequences();
97
98 sal_Int32 nColumnCount = aColumns.getLength();
99 sal_Int32 nRowCount = aRows.getLength();
100 if( nColumnCount>1 && nRowCount>1 )
101 {
102 //we have complex categories
103 //->split them in the direction of the first series
104 //detect whether the first series is a row or a column
105 bool bSeriesUsesColumns = true;
106 std::vector< rtl::Reference< DataSeries > > aSeries = ChartModelHelper::getDataSeries( &mrModel );
107 if( !aSeries.empty() )
108 {
109 rtl::Reference< DataSeries > xSeriesSource = aSeries.front();
110 OUString aStringDummy;
111 bool bDummy;
112 uno::Sequence< sal_Int32 > aSeqDummy;
113 DataSourceHelper::readArguments( xDataProvider->detectArguments( xSeriesSource),
114 aStringDummy, aSeqDummy, bSeriesUsesColumns, bDummy, bDummy );
115 }
116 if( bSeriesUsesColumns )
117 m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aColumns);
118 else
119 m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aRows);
120 }
121 }
122 }
123 if( m_aSplitCategoriesList.empty() )
124 {
125 m_aSplitCategoriesList = { m_xOriginalCategories };
126 }
127 }
128 }
129 catch( const uno::Exception & )
130 {
131 DBG_UNHANDLED_EXCEPTION("chart2");
132 }
133}
134
135ExplicitCategoriesProvider::~ExplicitCategoriesProvider()
136{
137}
138
139Reference< chart2::data::XDataSequence > ExplicitCategoriesProvider::getOriginalCategories()
140{
141 if( m_xOriginalCategories.is() )
142 return m_xOriginalCategories->getValues();
143 return nullptr;
144}
145
146bool ExplicitCategoriesProvider::hasComplexCategories() const
147{
148 return m_aSplitCategoriesList.size() > 1;
149}
150
151sal_Int32 ExplicitCategoriesProvider::getCategoryLevelCount() const
152{
153 sal_Int32 nCount = m_aSplitCategoriesList.size();
154 if(!nCount)
155 nCount = 1;
156 return nCount;
157}
158
159static std::vector<sal_Int32> lcl_getLimitingBorders( const std::vector< ComplexCategory >& rComplexCategories )
160{
161 std::vector<sal_Int32> aLimitingBorders;
162 sal_Int32 nBorderIndex = 0; /*border below the index*/
163 for (auto const& complexCategory : rComplexCategories)
164 {
165 nBorderIndex += complexCategory.Count;
166 aLimitingBorders.push_back(nBorderIndex);
167 }
168 return aLimitingBorders;
169}
170
171void ExplicitCategoriesProvider::convertCategoryAnysToText( uno::Sequence< OUString >& rOutTexts, const uno::Sequence< uno::Any >& rInAnys, ChartModel& rModel )
172{
173 sal_Int32 nCount = rInAnys.getLength();
174 if(!nCount)
175 return;
176 rOutTexts.realloc(nCount);
177 auto pOutTexts = rOutTexts.getArray();
178
179 sal_Int32 nAxisNumberFormat = 0;
180 rtl::Reference< BaseCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
181 if( xCooSysModel.is() )
182 {
183 rtl::Reference< Axis > xAxis = xCooSysModel->getAxisByDimension2(0,0);
184 nAxisNumberFormat = AxisHelper::getExplicitNumberFormatKeyForAxis(
185 xAxis, xCooSysModel, &rModel, false );
186 }
187
188 Color nLabelColor;
189 bool bColorChanged = false;
190
191 NumberFormatterWrapper aNumberFormatterWrapper( rModel.getNumberFormatsSupplier() );
192
193 for(sal_Int32 nN=0;nN<nCount;nN++)
194 {
195 OUString aText;
196 uno::Any aAny = rInAnys[nN];
197 if( aAny.hasValue() )
198 {
199 double fDouble = 0;
200 if( aAny>>=fDouble )
201 {
202 if( !std::isnan(fDouble) )
203 aText = aNumberFormatterWrapper.getFormattedString(
204 nAxisNumberFormat, fDouble, nLabelColor, bColorChanged );
205 }
206 else
207 {
208 aAny>>=aText;
209 }
210 }
211 pOutTexts[nN] = aText;
212 }
213}
214
215SplitCategoriesProvider::~SplitCategoriesProvider()
216{
217}
218
219namespace {
220
221class SplitCategoriesProvider_ForLabeledDataSequences : public SplitCategoriesProvider
222{
223public:
224
225 explicit SplitCategoriesProvider_ForLabeledDataSequences(
226 const std::vector< Reference< data::XLabeledDataSequence> >& rSplitCategoriesList
227 , ChartModel& rModel )
228 : m_rSplitCategoriesList( rSplitCategoriesList )
229 , mrModel( rModel )
230 {}
231
232 virtual sal_Int32 getLevelCount() const override;
233 virtual uno::Sequence< OUString > getStringsForLevel( sal_Int32 nIndex ) const override;
234
235private:
236 const std::vector< Reference< data::XLabeledDataSequence> >& m_rSplitCategoriesList;
237
238 ChartModel& mrModel;
239};
240
241}
242
243sal_Int32 SplitCategoriesProvider_ForLabeledDataSequences::getLevelCount() const
244{
245 return m_rSplitCategoriesList.size();
246}
247uno::Sequence< OUString > SplitCategoriesProvider_ForLabeledDataSequences::getStringsForLevel( sal_Int32 nLevel ) const
248{
249 uno::Sequence< OUString > aRet;
250 Reference< data::XLabeledDataSequence > xLabeledDataSequence( m_rSplitCategoriesList[nLevel] );
251 if( xLabeledDataSequence.is() )
252 {
253 uno::Reference< data::XDataSequence > xDataSequence( xLabeledDataSequence->getValues() );
254 if( xDataSequence.is() )
255 ExplicitCategoriesProvider::convertCategoryAnysToText( aRet, xDataSequence->getData(), mrModel );
256 }
257 return aRet;
258}
259
260static std::vector< ComplexCategory > lcl_DataSequenceToComplexCategoryVector(
261 const uno::Sequence< OUString >& rStrings
262 , const std::vector<sal_Int32>& rLimitingBorders, bool bCreateSingleCategories )
263{
264 std::vector< ComplexCategory > aResult;
265
266 sal_Int32 nMaxCount = rStrings.getLength();
267 OUString aPrevious;
268 sal_Int32 nCurrentCount=0;
269 for( sal_Int32 nN=0; nN<nMaxCount; nN++ )
270 {
271 const OUString& aCurrent = rStrings[nN];
272 if( bCreateSingleCategories || std::find( rLimitingBorders.begin(), rLimitingBorders.end(), nN ) != rLimitingBorders.end() )
273 {
274 aResult.emplace_back(aPrevious,nCurrentCount );
275 nCurrentCount=1;
276 aPrevious = aCurrent;
277 }
278 else
279 {
280 // Empty value is interpreted as a continuation of the previous
281 // category. Note that having the same value as the previous one
282 // does not equate to a continuation of the category.
283
284 if (aCurrent.isEmpty())
285 ++nCurrentCount;
286 else
287 {
288 aResult.emplace_back(aPrevious,nCurrentCount );
289 nCurrentCount=1;
290 aPrevious = aCurrent;
291 }
292 }
293 }
294 if( nCurrentCount )
295 aResult.emplace_back(aPrevious,nCurrentCount );
296
297 return aResult;
298}
299
300static sal_Int32 lcl_getCategoryCount( std::vector< ComplexCategory >& rComplexCategories )
301{
302 sal_Int32 nCount = 0;
303 for (auto const& complexCategory : rComplexCategories)
304 nCount+=complexCategory.Count;
305 return nCount;
306}
307
309 const SplitCategoriesProvider& rSplitCategoriesProvider,
310 std::vector< std::vector< ComplexCategory > >& rComplexCats )
311{
313
314 rComplexCats.clear();
315 sal_Int32 nLCount = rSplitCategoriesProvider.getLevelCount();
316 for( sal_Int32 nL = 0; nL < nLCount; nL++ )
317 {
318 std::vector<sal_Int32> aLimitingBorders;
319 if(nL>0)
320 aLimitingBorders = lcl_getLimitingBorders( rComplexCats.back() );
321 rComplexCats.push_back( lcl_DataSequenceToComplexCategoryVector(
322 rSplitCategoriesProvider.getStringsForLevel(nL), aLimitingBorders, nL==(nLCount-1) ) );
323 }
324
325 //ensure that the category count is the same on each level
326 sal_Int32 nMaxCategoryCount = 0;
327 {
328 for (auto & complexCat : rComplexCats)
329 {
330 sal_Int32 nCurrentCount = lcl_getCategoryCount(complexCat);
331 nMaxCategoryCount = std::max( nCurrentCount, nMaxCategoryCount );
332 }
333 for (auto & complexCat : rComplexCats)
334 {
335 if ( !complexCat.empty() )
336 {
337 sal_Int32 nCurrentCount = lcl_getCategoryCount(complexCat);
338 if( nCurrentCount< nMaxCategoryCount )
339 {
340 ComplexCategory& rComplexCategory = complexCat.back();
341 rComplexCategory.Count += (nMaxCategoryCount-nCurrentCount);
342 }
343 }
344 }
345 }
346
347 //create a list with an element for every index
348 std::vector< std::vector< ComplexCategory > > aComplexCatsPerIndex;
349 for (auto const& complexCat : rComplexCats)
350 {
351 std::vector< ComplexCategory > aSingleLevel;
352 for (auto const& elem : complexCat)
353 {
354 sal_Int32 nCount = elem.Count;
355 while( nCount-- )
356 aSingleLevel.push_back(elem);
357 }
358 aComplexCatsPerIndex.push_back( aSingleLevel );
359 }
360
361 if(nMaxCategoryCount)
362 {
363 aRet.realloc(nMaxCategoryCount);
364 auto pRet = aRet.getArray();
365 for(sal_Int32 nN=0; nN<nMaxCategoryCount; nN++)
366 {
367 OUStringBuffer aText;
368 for (auto const& complexCatPerIndex : aComplexCatsPerIndex)
369 {
370 if ( o3tl::make_unsigned(nN) < complexCatPerIndex.size() )
371 {
372 OUString aAddText = complexCatPerIndex[nN].Text;
373 if( !aAddText.isEmpty() )
374 {
375 if(!aText.isEmpty())
376 aText.append(" ");
377 aText.append(aAddText);
378 }
379 }
380 }
381 pRet[nN]=aText.makeStringAndClear();
382 }
383 }
384 return aRet;
385}
386
387Sequence< OUString > ExplicitCategoriesProvider::getExplicitSimpleCategories(
388 const SplitCategoriesProvider& rSplitCategoriesProvider )
389{
390 vector< vector< ComplexCategory > > aComplexCats;
391 return lcl_getExplicitSimpleCategories( rSplitCategoriesProvider, aComplexCats );
392}
393
394static bool lcl_fillDateCategories( const uno::Reference< data::XDataSequence >& xDataSequence, std::vector< double >& rDateCategories, bool bIsAutoDate, ChartModel& rModel )
395{
396 bool bOnlyDatesFound = true;
397 bool bAnyDataFound = false;
398
399 if( xDataSequence.is() )
400 {
401 uno::Sequence< uno::Any > aValues = xDataSequence->getData();
402 sal_Int32 nCount = aValues.getLength();
403 rDateCategories.reserve(nCount);
404 Reference< util::XNumberFormats > xNumberFormats( rModel.getNumberFormats() );
405
406 bool bOwnData = false;
407 bool bOwnDataAnddAxisHasAnyFormat = false;
408 bool bOwnDataAnddAxisHasDateFormat = false;
409 rtl::Reference< BaseCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
410 if( xCooSysModel.is() )
411 {
412 if( rModel.hasInternalDataProvider() )
413 {
414 bOwnData = true;
415 rtl::Reference< Axis > xAxisProps = xCooSysModel->getAxisByDimension2(0,0);
416 sal_Int32 nAxisNumberFormat = 0;
417 if (xAxisProps.is() && (xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nAxisNumberFormat))
418 {
419 bOwnDataAnddAxisHasAnyFormat = true;
420 bOwnDataAnddAxisHasDateFormat = DiagramHelper::isDateNumberFormat( nAxisNumberFormat, xNumberFormats );
421 }
422 }
423 }
424
425 for(sal_Int32 nN=0;nN<nCount;nN++)
426 {
427 bool bIsDate = false;
428 if( bIsAutoDate )
429 {
430 if( bOwnData )
431 bIsDate = !bOwnDataAnddAxisHasAnyFormat || bOwnDataAnddAxisHasDateFormat;
432 else
433 bIsDate = DiagramHelper::isDateNumberFormat( xDataSequence->getNumberFormatKeyByIndex( nN ), xNumberFormats );
434 }
435 else
436 bIsDate = true;
437
438 bool bContainsEmptyString = false;
439 uno::Any aAny = aValues[nN];
440 if( aAny.hasValue() )
441 {
442 OUString aTest;
443 double fTest = 0;
444 bool bContainsNan = false;
445 if( (aAny>>=aTest) && aTest.isEmpty() ) //empty String
446 bContainsEmptyString = true;
447 else if( (aAny>>=fTest) && std::isnan(fTest) )
448 bContainsNan = true;
449
450 if( !bContainsEmptyString && !bContainsNan )
451 bAnyDataFound = true;
452 }
453 double aDate( 1.0 );
454 if( bIsDate && (aAny >>= aDate) )
455 rDateCategories.push_back( aDate );
456 else
457 {
458 if( aAny.hasValue() && !bContainsEmptyString )//empty string does not count as non date value!
459 bOnlyDatesFound=false;
460 rDateCategories.push_back( std::numeric_limits<double>::quiet_NaN() );
461 }
462 }
463 std::sort( rDateCategories.begin(), rDateCategories.end() );
464 }
465
466 return bAnyDataFound && bOnlyDatesFound;
467}
468
469void ExplicitCategoriesProvider::init()
470{
471 if( !m_bDirty )
472 return;
473
474 m_aComplexCats.clear();//not one per index
475 m_aDateCategories.clear();
476
477 if( m_xOriginalCategories.is() )
478 {
479 if( !hasComplexCategories() )
480 {
481 if(m_bIsDateAxis)
482 {
483 if( ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( m_xCooSysModel.get(), 0 ), 0 ) )
484 m_bIsDateAxis = lcl_fillDateCategories( m_xOriginalCategories->getValues(), m_aDateCategories, m_bIsAutoDate, mrModel );
485 else
486 m_bIsDateAxis = false;
487 }
488 }
489 else
490 {
491 m_bIsDateAxis = false;
492 }
493 }
494 else
495 m_bIsDateAxis=false;
496 m_bDirty = false;
497}
498
499Sequence< OUString > const & ExplicitCategoriesProvider::getSimpleCategories()
500{
501 if( !m_bIsExplicitCategoriesInited )
502 {
503 init();
504 m_aExplicitCategories.realloc(0);
505 if( m_xOriginalCategories.is() )
506 {
507 if( !hasComplexCategories() )
508 {
509 uno::Reference< data::XDataSequence > xDataSequence( m_xOriginalCategories->getValues() );
510 if( xDataSequence.is() )
511 ExplicitCategoriesProvider::convertCategoryAnysToText( m_aExplicitCategories, xDataSequence->getData(), mrModel );
512 }
513 else
514 {
515 m_aExplicitCategories = lcl_getExplicitSimpleCategories(
516 SplitCategoriesProvider_ForLabeledDataSequences( m_aSplitCategoriesList, mrModel ), m_aComplexCats );
517 }
518 }
519 if(!m_aExplicitCategories.hasElements())
520 m_aExplicitCategories = DiagramHelper::generateAutomaticCategoriesFromCooSys( m_xCooSysModel.get() );
521 m_bIsExplicitCategoriesInited = true;
522 }
523 return m_aExplicitCategories;
524}
525
526const std::vector<ComplexCategory>* ExplicitCategoriesProvider::getCategoriesByLevel( sal_Int32 nLevel )
527{
528 init();
529 sal_Int32 nMaxIndex = m_aComplexCats.size()-1;
530 if (nLevel >= 0 && nLevel <= nMaxIndex)
531 return &m_aComplexCats[nMaxIndex-nLevel];
532 return nullptr;
533}
534
535OUString ExplicitCategoriesProvider::getCategoryByIndex(
537 , ChartModel& rModel
538 , sal_Int32 nIndex )
539{
540 if( xCooSysModel.is())
541 {
542 ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSysModel, rModel );
543 Sequence< OUString > aCategories( aExplicitCategoriesProvider.getSimpleCategories());
544 if( nIndex < aCategories.getLength())
545 return aCategories[ nIndex ];
546 }
547 return OUString();
548}
549
550bool ExplicitCategoriesProvider::isDateAxis()
551{
552 init();
553 return m_bIsDateAxis;
554}
555
556const std::vector< double >& ExplicitCategoriesProvider::getDateCategories()
557{
558 init();
559 return m_aDateCategories;
560}
561
562} //namespace chart
563
564/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const std::vector< Reference< data::XLabeledDataSequence > > & m_rSplitCategoriesList
ChartModel & mrModel
FILE * init(int, char **)
ExplicitCategoriesProvider(const rtl::Reference< ::chart::BaseCoordinateSystem > &xCooSysModel, ChartModel &rChartModel)
css::uno::Sequence< OUString > const & getSimpleCategories()
OUString getFormattedString(sal_Int32 nNumberFormatKey, double fValue, Color &rLabelColor, bool &rbColorChanged) const
virtual css::uno::Sequence< OUString > getStringsForLevel(sal_Int32 nIndex) const =0
virtual sal_Int32 getLevelCount() const =0
int nCount
#define DBG_UNHANDLED_EXCEPTION(...)
sal_Int32 nIndex
static std::vector< ComplexCategory > lcl_DataSequenceToComplexCategoryVector(const uno::Sequence< OUString > &rStrings, const std::vector< sal_Int32 > &rLimitingBorders, bool bCreateSingleCategories)
static std::vector< sal_Int32 > lcl_getLimitingBorders(const std::vector< ComplexCategory > &rComplexCategories)
static Sequence< OUString > lcl_getExplicitSimpleCategories(const SplitCategoriesProvider &rSplitCategoriesProvider, std::vector< std::vector< ComplexCategory > > &rComplexCats)
static sal_Int32 lcl_getCategoryCount(std::vector< ComplexCategory > &rComplexCategories)
static bool lcl_fillDateCategories(const uno::Reference< data::XDataSequence > &xDataSequence, std::vector< double > &rDateCategories, bool bIsAutoDate, ChartModel &rModel)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
bool hasValue()
constexpr OUStringLiteral CHART_UNONAME_NUMFMT
Definition: unonames.hxx:20