1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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>
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
;
46 ExplicitCategoriesProvider::ExplicitCategoriesProvider( const Reference
< chart2::XCoordinateSystem
>& xCooSysModel
47 , ChartModel
& rModel
)
49 , m_xCooSysModel( xCooSysModel
)
51 , m_xOriginalCategories()
52 , m_bIsExplicitCategoriesInited(false)
53 , m_bIsDateAxis(false)
54 , m_bIsAutoDate(false)
58 if( xCooSysModel
.is() )
60 uno::Reference
< XAxis
> xAxis( xCooSysModel
->getAxisByDimension(0,0) );
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
;
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
;
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();
143 bool ExplicitCategoriesProvider::hasComplexCategories() const
145 return m_aSplitCategoriesList
.getLength() > 1;
148 sal_Int32
ExplicitCategoriesProvider::getCategoryLevelCount() const
150 sal_Int32 nCount
= m_aSplitCategoriesList
.getLength();
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();
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
++)
197 uno::Any aAny
= rInAnys
[nN
];
198 if( aAny
.hasValue() )
203 if( !::rtl::math::isNan(fDouble
) )
204 aText
= aNumberFormatterWrapper
.getFormattedString(
205 nAxisNumberFormat
, fDouble
, nLabelColor
, bColorChanged
);
212 rOutTexts
[nN
] = aText
;
216 SplitCategoriesProvider::~SplitCategoriesProvider()
220 class SplitCategoriesProvider_ForLabeledDataSequences
: public SplitCategoriesProvider
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
)
232 virtual ~SplitCategoriesProvider_ForLabeledDataSequences()
235 virtual sal_Int32
getLevelCount() const SAL_OVERRIDE
;
236 virtual uno::Sequence
< OUString
> getStringsForLevel( sal_Int32 nIndex
) const SAL_OVERRIDE
;
239 const ::com::sun::star::uno::Sequence
< ::com::sun::star::uno::Reference
<
240 ::com::sun::star::chart2::data::XLabeledDataSequence
> >& m_rSplitCategoriesList
;
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
);
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();
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
) );
278 aPrevious
= aCurrent
;
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())
290 aResult
.push_back( ComplexCategory(aPrevious
,nCurrentCount
) );
292 aPrevious
= aCurrent
;
297 aResult
.push_back( ComplexCategory(aPrevious
,nCurrentCount
) );
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
)
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
;
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
;
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
++)
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() )
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() )
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;
452 bIsDate
= !bOwnDataAnddAxisHasAnyFormat
|| bOwnDataAnddAxisHasDateFormat
;
454 bIsDate
= DiagramHelper::isDateNumberFormat( xDataSequence
->getNumberFormatKeyByIndex( nN
), xNumberFormats
);
459 bool bContainsEmptyString
= false;
460 uno::Any aAny
= aValues
[nN
];
461 if( aAny
.hasValue() )
465 bool bContainsNan
= false;
466 if( (aAny
>>=aTest
) && aTest
.isEmpty() ) //empty String
467 bContainsEmptyString
= true;
468 else if( (aAny
>>=fTest
) && ::rtl::math::isNan(fTest
) )
471 if( !bContainsEmptyString
&& !bContainsNan
)
472 bAnyDataFound
= true;
474 DatePlusIndex
aDatePlusIndex( 1.0, nN
);
475 if( bIsDate
&& (aAny
>>= aDatePlusIndex
.fValue
) )
476 rDateCategories
.push_back( aDatePlusIndex
);
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()
495 m_aComplexCats
.clear();//not one per index
496 m_aDateCategories
.clear();
498 if( m_xOriginalCategories
.is() )
500 if( !hasComplexCategories() )
504 if( ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( m_xCooSysModel
, 0 ), 2, 0 ) )
505 m_bIsDateAxis
= lcl_fillDateCategories( m_xOriginalCategories
->getValues(), m_aDateCategories
, m_bIsAutoDate
, mrModel
);
507 m_bIsDateAxis
= false;
512 m_bIsDateAxis
= false;
521 Sequence
< OUString
> ExplicitCategoriesProvider::getSimpleCategories()
523 if( !m_bIsExplicitCategoriesInited
)
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
);
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
)
551 sal_Int32 nMaxIndex
= m_aComplexCats
.size()-1;
552 if (nLevel
>= 0 && nLevel
<= nMaxIndex
)
553 return &m_aComplexCats
[nMaxIndex
-nLevel
];
557 OUString
ExplicitCategoriesProvider::getCategoryByIndex(
558 const Reference
< XCoordinateSystem
>& xCooSysModel
562 if( xCooSysModel
.is())
564 ExplicitCategoriesProvider
aExplicitCategoriesProvider( xCooSysModel
, rModel
);
565 Sequence
< OUString
> aCategories( aExplicitCategoriesProvider
.getSimpleCategories());
566 if( nIndex
< aCategories
.getLength())
567 return aCategories
[ nIndex
];
572 bool ExplicitCategoriesProvider::isDateAxis()
575 return m_bIsDateAxis
;
578 const std::vector
< DatePlusIndex
>& ExplicitCategoriesProvider::getDateCategories()
581 return m_aDateCategories
;
586 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */