bump product version to 5.0.4.1
[LibreOffice.git] / chart2 / source / tools / ExplicitCategoriesProvider.cxx
blob8684e08fe52a9fc38703ecabd2d70dcce5337f00
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "ExplicitCategoriesProvider.hxx"
21 #include "DiagramHelper.hxx"
22 #include "ChartTypeHelper.hxx"
23 #include "AxisHelper.hxx"
24 #include "CommonConverters.hxx"
25 #include "DataSourceHelper.hxx"
26 #include "ChartModelHelper.hxx"
27 #include "ContainerHelper.hxx"
28 #include "macros.hxx"
29 #include "NumberFormatterWrapper.hxx"
30 #include <unonames.hxx>
32 #include <com/sun/star/chart2/AxisType.hpp>
33 #include <com/sun/star/util/NumberFormat.hpp>
34 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
35 #include <com/sun/star/frame/XModel.hpp>
37 namespace chart
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::chart2;
42 using ::com::sun::star::uno::Reference;
43 using ::com::sun::star::uno::Sequence;
44 using ::std::vector;
46 ExplicitCategoriesProvider::ExplicitCategoriesProvider( const Reference< chart2::XCoordinateSystem >& xCooSysModel
47 , ChartModel& rModel )
48 : m_bDirty(true)
49 , m_xCooSysModel( xCooSysModel )
50 , mrModel(rModel)
51 , m_xOriginalCategories()
52 , m_bIsExplicitCategoriesInited(false)
53 , m_bIsDateAxis(false)
54 , m_bIsAutoDate(false)
56 try
58 if( xCooSysModel.is() )
60 uno::Reference< XAxis > xAxis( xCooSysModel->getAxisByDimension(0,0) );
61 if( xAxis.is() )
63 ScaleData aScale( xAxis->getScaleData() );
64 m_xOriginalCategories = aScale.Categories;
65 m_bIsAutoDate = (aScale.AutoDateAxis && aScale.AxisType==chart2::AxisType::CATEGORY);
66 m_bIsDateAxis = (aScale.AxisType == chart2::AxisType::DATE || m_bIsAutoDate);
70 if( m_xOriginalCategories.is() )
72 uno::Reference< data::XDataProvider > xDataProvider( mrModel.getDataProvider() );
74 OUString aCategoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories ) );
75 if( xDataProvider.is() && !aCategoriesRange.isEmpty() )
77 const bool bFirstCellAsLabel = false;
78 const bool bHasCategories = false;
79 const uno::Sequence< sal_Int32 > aSequenceMapping;
81 uno::Reference< data::XDataSource > xColumnCategoriesSource( xDataProvider->createDataSource(
82 DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, true /*bUseColumns*/
83 , bFirstCellAsLabel, bHasCategories ) ) );
85 uno::Reference< data::XDataSource > xRowCategoriesSource( xDataProvider->createDataSource(
86 DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, false /*bUseColumns*/
87 , bFirstCellAsLabel, bHasCategories ) ) );
89 if( xColumnCategoriesSource.is() && xRowCategoriesSource.is() )
91 Sequence< Reference< data::XLabeledDataSequence> > aColumns = xColumnCategoriesSource->getDataSequences();
92 Sequence< Reference< data::XLabeledDataSequence> > aRows = xRowCategoriesSource->getDataSequences();
94 sal_Int32 nColumnCount = aColumns.getLength();
95 sal_Int32 nRowCount = aRows.getLength();
96 if( nColumnCount>1 && nRowCount>1 )
98 //we have complex categories
99 //->split them in the direction of the first series
100 //detect whether the first series is a row or a column
101 bool bSeriesUsesColumns = true;
102 ::std::vector< Reference< XDataSeries > > aSeries( ChartModelHelper::getDataSeries( mrModel ) );
103 if( !aSeries.empty() )
105 uno::Reference< data::XDataSource > xSeriesSource( aSeries.front(), uno::UNO_QUERY );
106 OUString aStringDummy;
107 bool bDummy;
108 uno::Sequence< sal_Int32 > aSeqDummy;
109 DataSourceHelper::readArguments( xDataProvider->detectArguments( xSeriesSource),
110 aStringDummy, aSeqDummy, bSeriesUsesColumns, bDummy, bDummy );
112 if( bSeriesUsesColumns )
113 m_aSplitCategoriesList=aColumns;
114 else
115 m_aSplitCategoriesList=aRows;
119 if( !m_aSplitCategoriesList.getLength() )
121 m_aSplitCategoriesList.realloc(1);
122 m_aSplitCategoriesList[0]=m_xOriginalCategories;
126 catch( const uno::Exception & ex )
128 ASSERT_EXCEPTION( ex );
132 ExplicitCategoriesProvider::~ExplicitCategoriesProvider()
136 Reference< chart2::data::XDataSequence > ExplicitCategoriesProvider::getOriginalCategories()
138 if( m_xOriginalCategories.is() )
139 return m_xOriginalCategories->getValues();
140 return 0;
143 bool ExplicitCategoriesProvider::hasComplexCategories() const
145 return m_aSplitCategoriesList.getLength() > 1;
148 sal_Int32 ExplicitCategoriesProvider::getCategoryLevelCount() const
150 sal_Int32 nCount = m_aSplitCategoriesList.getLength();
151 if(!nCount)
152 nCount = 1;
153 return nCount;
156 std::vector<sal_Int32> lcl_getLimitingBorders( const std::vector< ComplexCategory >& rComplexCategories )
158 std::vector<sal_Int32> aLimitingBorders;
159 std::vector< ComplexCategory >::const_iterator aIt( rComplexCategories.begin() );
160 std::vector< ComplexCategory >::const_iterator aEnd( rComplexCategories.end() );
161 sal_Int32 nBorderIndex = 0; /*border below the index*/
162 for( ; aIt != aEnd; ++aIt )
164 ComplexCategory aComplexCategory(*aIt);
165 nBorderIndex += aComplexCategory.Count;
166 aLimitingBorders.push_back(nBorderIndex);
168 return aLimitingBorders;
171 void ExplicitCategoriesProvider::convertCategoryAnysToText( uno::Sequence< OUString >& rOutTexts, const uno::Sequence< uno::Any >& rInAnys, ChartModel& rModel )
173 sal_Int32 nCount = rInAnys.getLength();
174 if(!nCount)
175 return;
176 rOutTexts.realloc(nCount);
177 Reference< util::XNumberFormats > xNumberFormats;
178 xNumberFormats = Reference< util::XNumberFormats >( rModel.getNumberFormats() );
180 sal_Int32 nAxisNumberFormat = 0;
181 Reference< XCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( rModel ) );
182 if( xCooSysModel.is() )
184 Reference< chart2::XAxis > xAxis( xCooSysModel->getAxisByDimension(0,0) );
185 nAxisNumberFormat = AxisHelper::getExplicitNumberFormatKeyForAxis(
186 xAxis, xCooSysModel, uno::Reference<chart2::XChartDocument>(static_cast< ::cppu::OWeakObject* >(&rModel), uno::UNO_QUERY), false );
189 sal_Int32 nLabelColor;
190 bool bColorChanged = false;
192 NumberFormatterWrapper aNumberFormatterWrapper( rModel.getNumberFormatsSupplier() );
194 for(sal_Int32 nN=0;nN<nCount;nN++)
196 OUString aText;
197 uno::Any aAny = rInAnys[nN];
198 if( aAny.hasValue() )
200 double fDouble = 0;
201 if( aAny>>=fDouble )
203 if( !::rtl::math::isNan(fDouble) )
204 aText = aNumberFormatterWrapper.getFormattedString(
205 nAxisNumberFormat, fDouble, nLabelColor, bColorChanged );
207 else
209 aAny>>=aText;
212 rOutTexts[nN] = aText;
216 SplitCategoriesProvider::~SplitCategoriesProvider()
220 class SplitCategoriesProvider_ForLabeledDataSequences : public SplitCategoriesProvider
222 public:
224 explicit SplitCategoriesProvider_ForLabeledDataSequences(
225 const ::com::sun::star::uno::Sequence<
226 ::com::sun::star::uno::Reference<
227 ::com::sun::star::chart2::data::XLabeledDataSequence> >& rSplitCategoriesList
228 , ChartModel& rModel )
229 : m_rSplitCategoriesList( rSplitCategoriesList )
230 , mrModel( rModel )
232 virtual ~SplitCategoriesProvider_ForLabeledDataSequences()
235 virtual sal_Int32 getLevelCount() const SAL_OVERRIDE;
236 virtual uno::Sequence< OUString > getStringsForLevel( sal_Int32 nIndex ) const SAL_OVERRIDE;
238 private:
239 const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference<
240 ::com::sun::star::chart2::data::XLabeledDataSequence> >& m_rSplitCategoriesList;
242 ChartModel& mrModel;
245 sal_Int32 SplitCategoriesProvider_ForLabeledDataSequences::getLevelCount() const
247 return m_rSplitCategoriesList.getLength();
249 uno::Sequence< OUString > SplitCategoriesProvider_ForLabeledDataSequences::getStringsForLevel( sal_Int32 nLevel ) const
251 uno::Sequence< OUString > aRet;
252 Reference< data::XLabeledDataSequence > xLabeledDataSequence( m_rSplitCategoriesList[nLevel] );
253 if( xLabeledDataSequence.is() )
255 uno::Reference< data::XDataSequence > xDataSequence( xLabeledDataSequence->getValues() );
256 if( xDataSequence.is() )
257 ExplicitCategoriesProvider::convertCategoryAnysToText( aRet, xDataSequence->getData(), mrModel );
259 return aRet;
262 std::vector< ComplexCategory > lcl_DataSequenceToComplexCategoryVector(
263 const uno::Sequence< OUString >& rStrings
264 , const std::vector<sal_Int32>& rLimitingBorders, bool bCreateSingleCategories )
266 std::vector< ComplexCategory > aResult;
268 sal_Int32 nMaxCount = rStrings.getLength();
269 OUString aPrevious;
270 sal_Int32 nCurrentCount=0;
271 for( sal_Int32 nN=0; nN<nMaxCount; nN++ )
273 const OUString& aCurrent = rStrings[nN];
274 if( bCreateSingleCategories || ::std::find( rLimitingBorders.begin(), rLimitingBorders.end(), nN ) != rLimitingBorders.end() )
276 aResult.push_back( ComplexCategory(aPrevious,nCurrentCount) );
277 nCurrentCount=1;
278 aPrevious = aCurrent;
280 else
282 // Empty value is interpreted as a continuation of the previous
283 // category. Note that having the same value as the previous one
284 // does not equate to a continuation of the category.
286 if (aCurrent.isEmpty())
287 ++nCurrentCount;
288 else
290 aResult.push_back( ComplexCategory(aPrevious,nCurrentCount) );
291 nCurrentCount=1;
292 aPrevious = aCurrent;
296 if( nCurrentCount )
297 aResult.push_back( ComplexCategory(aPrevious,nCurrentCount) );
299 return aResult;
302 sal_Int32 lcl_getCategoryCount( std::vector< ComplexCategory >& rComplexCategories )
304 sal_Int32 nCount = 0;
305 std::vector< ComplexCategory >::const_iterator aIt( rComplexCategories.begin() );
306 std::vector< ComplexCategory >::const_iterator aEnd( rComplexCategories.end() );
307 for( ; aIt != aEnd; ++aIt )
308 nCount+=aIt->Count;
309 return nCount;
312 Sequence< OUString > lcl_getExplicitSimpleCategories(
313 const SplitCategoriesProvider& rSplitCategoriesProvider,
314 ::std::vector< ::std::vector< ComplexCategory > >& rComplexCats )
316 Sequence< OUString > aRet;
318 rComplexCats.clear();
319 sal_Int32 nLCount = rSplitCategoriesProvider.getLevelCount();
320 for( sal_Int32 nL = 0; nL < nLCount; nL++ )
322 std::vector<sal_Int32> aLimitingBorders;
323 if(nL>0)
324 aLimitingBorders = lcl_getLimitingBorders( rComplexCats.back() );
325 rComplexCats.push_back( lcl_DataSequenceToComplexCategoryVector(
326 rSplitCategoriesProvider.getStringsForLevel(nL), aLimitingBorders, nL==(nLCount-1) ) );
329 std::vector< std::vector< ComplexCategory > >::iterator aOuterIt( rComplexCats.begin() );
330 std::vector< std::vector< ComplexCategory > >::const_iterator aOuterEnd( rComplexCats.end() );
332 //ensure that the category count is the same on each level
333 sal_Int32 nMaxCategoryCount = 0;
335 for( aOuterIt=rComplexCats.begin(); aOuterIt != aOuterEnd; ++aOuterIt )
337 sal_Int32 nCurrentCount = lcl_getCategoryCount( *aOuterIt );
338 nMaxCategoryCount = std::max( nCurrentCount, nMaxCategoryCount );
340 for( aOuterIt=rComplexCats.begin(); aOuterIt != aOuterEnd; ++aOuterIt )
342 if ( !aOuterIt->empty() )
344 sal_Int32 nCurrentCount = lcl_getCategoryCount( *aOuterIt );
345 if( nCurrentCount< nMaxCategoryCount )
347 ComplexCategory& rComplexCategory = aOuterIt->back();
348 rComplexCategory.Count += (nMaxCategoryCount-nCurrentCount);
354 //create a list with an element for every index
355 std::vector< std::vector< ComplexCategory > > aComplexCatsPerIndex;
356 for( aOuterIt=rComplexCats.begin() ; aOuterIt != aOuterEnd; ++aOuterIt )
358 std::vector< ComplexCategory > aSingleLevel;
359 std::vector< ComplexCategory >::iterator aIt( aOuterIt->begin() );
360 std::vector< ComplexCategory >::const_iterator aEnd( aOuterIt->end() );
361 for( ; aIt != aEnd; ++aIt )
363 ComplexCategory aComplexCategory( *aIt );
364 sal_Int32 nCount = aComplexCategory.Count;
365 while( nCount-- )
366 aSingleLevel.push_back(aComplexCategory);
368 aComplexCatsPerIndex.push_back( aSingleLevel );
371 if(nMaxCategoryCount)
373 aRet.realloc(nMaxCategoryCount);
374 aOuterEnd = aComplexCatsPerIndex.end();
375 OUString aSpace(" ");
376 for(sal_Int32 nN=0; nN<nMaxCategoryCount; nN++)
378 OUString aText;
379 for( aOuterIt=aComplexCatsPerIndex.begin() ; aOuterIt != aOuterEnd; ++aOuterIt )
381 if ( static_cast<size_t>(nN) < aOuterIt->size() )
383 OUString aAddText = (*aOuterIt)[nN].Text;
384 if( !aAddText.isEmpty() )
386 if(!aText.isEmpty())
387 aText += aSpace;
388 aText += aAddText;
392 aRet[nN]=aText;
395 return aRet;
398 Sequence< OUString > ExplicitCategoriesProvider::getExplicitSimpleCategories(
399 const SplitCategoriesProvider& rSplitCategoriesProvider )
401 vector< vector< ComplexCategory > > aComplexCats;
402 return lcl_getExplicitSimpleCategories( rSplitCategoriesProvider, aComplexCats );
405 struct DatePlusIndexComparator
407 inline bool operator() ( const DatePlusIndex& aFirst,
408 const DatePlusIndex& aSecond ) const
410 return ( aFirst.fValue < aSecond.fValue );
414 bool lcl_fillDateCategories( const uno::Reference< data::XDataSequence >& xDataSequence, std::vector< DatePlusIndex >& rDateCategories, bool bIsAutoDate, ChartModel& rModel )
416 bool bOnlyDatesFound = true;
417 bool bAnyDataFound = false;
419 if( xDataSequence.is() )
421 uno::Sequence< uno::Any > aValues = xDataSequence->getData();
422 sal_Int32 nCount = aValues.getLength();
423 rDateCategories.reserve(nCount);
424 Reference< util::XNumberFormats > xNumberFormats;
425 xNumberFormats = Reference< util::XNumberFormats >( rModel.getNumberFormats() );
427 bool bOwnData = false;
428 bool bOwnDataAnddAxisHasAnyFormat = false;
429 bool bOwnDataAnddAxisHasDateFormat = false;
430 Reference< XCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( rModel ) );
431 if( xCooSysModel.is() )
433 if( rModel.hasInternalDataProvider() )
435 bOwnData = true;
436 Reference< beans::XPropertySet > xAxisProps( xCooSysModel->getAxisByDimension(0,0), uno::UNO_QUERY );
437 sal_Int32 nAxisNumberFormat = 0;
438 if (xAxisProps.is() && (xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nAxisNumberFormat))
440 bOwnDataAnddAxisHasAnyFormat = true;
441 bOwnDataAnddAxisHasDateFormat = DiagramHelper::isDateNumberFormat( nAxisNumberFormat, xNumberFormats );
446 for(sal_Int32 nN=0;nN<nCount;nN++)
448 bool bIsDate = false;
449 if( bIsAutoDate )
451 if( bOwnData )
452 bIsDate = !bOwnDataAnddAxisHasAnyFormat || bOwnDataAnddAxisHasDateFormat;
453 else
454 bIsDate = DiagramHelper::isDateNumberFormat( xDataSequence->getNumberFormatKeyByIndex( nN ), xNumberFormats );
456 else
457 bIsDate = true;
459 bool bContainsEmptyString = false;
460 uno::Any aAny = aValues[nN];
461 if( aAny.hasValue() )
463 OUString aTest;
464 double fTest = 0;
465 bool bContainsNan = false;
466 if( (aAny>>=aTest) && aTest.isEmpty() ) //empty String
467 bContainsEmptyString = true;
468 else if( (aAny>>=fTest) && ::rtl::math::isNan(fTest) )
469 bContainsNan = true;
471 if( !bContainsEmptyString && !bContainsNan )
472 bAnyDataFound = true;
474 DatePlusIndex aDatePlusIndex( 1.0, nN );
475 if( bIsDate && (aAny >>= aDatePlusIndex.fValue) )
476 rDateCategories.push_back( aDatePlusIndex );
477 else
479 if( aAny.hasValue() && !bContainsEmptyString )//empty string does not count as non date value!
480 bOnlyDatesFound=false;
481 ::rtl::math::setNan( &aDatePlusIndex.fValue );
482 rDateCategories.push_back( aDatePlusIndex );
485 ::std::sort( rDateCategories.begin(), rDateCategories.end(), DatePlusIndexComparator() );
488 return bAnyDataFound && bOnlyDatesFound;
491 void ExplicitCategoriesProvider::init()
493 if( m_bDirty )
495 m_aComplexCats.clear();//not one per index
496 m_aDateCategories.clear();
498 if( m_xOriginalCategories.is() )
500 if( !hasComplexCategories() )
502 if(m_bIsDateAxis)
504 if( ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( m_xCooSysModel, 0 ), 2, 0 ) )
505 m_bIsDateAxis = lcl_fillDateCategories( m_xOriginalCategories->getValues(), m_aDateCategories, m_bIsAutoDate, mrModel );
506 else
507 m_bIsDateAxis = false;
510 else
512 m_bIsDateAxis = false;
515 else
516 m_bIsDateAxis=false;
517 m_bDirty = false;
521 Sequence< OUString > ExplicitCategoriesProvider::getSimpleCategories()
523 if( !m_bIsExplicitCategoriesInited )
525 init();
526 m_aExplicitCategories.realloc(0);
527 if( m_xOriginalCategories.is() )
529 if( !hasComplexCategories() )
531 uno::Reference< data::XDataSequence > xDataSequence( m_xOriginalCategories->getValues() );
532 if( xDataSequence.is() )
533 ExplicitCategoriesProvider::convertCategoryAnysToText( m_aExplicitCategories, xDataSequence->getData(), mrModel );
535 else
537 m_aExplicitCategories = lcl_getExplicitSimpleCategories(
538 SplitCategoriesProvider_ForLabeledDataSequences( m_aSplitCategoriesList, mrModel ), m_aComplexCats );
541 if(!m_aExplicitCategories.getLength())
542 m_aExplicitCategories = DiagramHelper::generateAutomaticCategoriesFromCooSys( m_xCooSysModel );
543 m_bIsExplicitCategoriesInited = true;
545 return m_aExplicitCategories;
548 const std::vector<ComplexCategory>* ExplicitCategoriesProvider::getCategoriesByLevel( sal_Int32 nLevel )
550 init();
551 sal_Int32 nMaxIndex = m_aComplexCats.size()-1;
552 if (nLevel >= 0 && nLevel <= nMaxIndex)
553 return &m_aComplexCats[nMaxIndex-nLevel];
554 return NULL;
557 OUString ExplicitCategoriesProvider::getCategoryByIndex(
558 const Reference< XCoordinateSystem >& xCooSysModel
559 , ChartModel& rModel
560 , sal_Int32 nIndex )
562 if( xCooSysModel.is())
564 ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSysModel, rModel );
565 Sequence< OUString > aCategories( aExplicitCategoriesProvider.getSimpleCategories());
566 if( nIndex < aCategories.getLength())
567 return aCategories[ nIndex ];
569 return OUString();
572 bool ExplicitCategoriesProvider::isDateAxis()
574 init();
575 return m_bIsDateAxis;
578 const std::vector< DatePlusIndex >& ExplicitCategoriesProvider::getDateCategories()
580 init();
581 return m_aDateCategories;
584 } //namespace chart
586 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */