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 .
21 #include "ExplicitCategoriesProvider.hxx"
22 #include "DiagramHelper.hxx"
23 #include "ChartTypeHelper.hxx"
24 #include "AxisHelper.hxx"
25 #include "CommonConverters.hxx"
26 #include "DataSourceHelper.hxx"
27 #include "ChartModelHelper.hxx"
28 #include "ContainerHelper.hxx"
30 #include "NumberFormatterWrapper.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>
36 //.............................................................................
39 //.............................................................................
41 using namespace ::com::sun::star
;
42 using namespace ::com::sun::star::chart2
;
43 using ::com::sun::star::uno::Reference
;
44 using ::com::sun::star::uno::Sequence
;
48 ExplicitCategoriesProvider::ExplicitCategoriesProvider( const Reference
< chart2::XCoordinateSystem
>& xCooSysModel
49 , const uno::Reference
< frame::XModel
>& xChartModel
)
51 , m_xCooSysModel( xCooSysModel
)
52 , m_xChartModel( xChartModel
)
53 , m_xOriginalCategories()
54 , m_bIsExplicitCategoriesInited(false)
55 , m_bIsDateAxis(false)
56 , m_bIsAutoDate(false)
60 if( xCooSysModel
.is() )
62 uno::Reference
< XAxis
> xAxis( xCooSysModel
->getAxisByDimension(0,0) );
65 ScaleData
aScale( xAxis
->getScaleData() );
66 m_xOriginalCategories
= aScale
.Categories
;
67 m_bIsAutoDate
= (aScale
.AutoDateAxis
&& aScale
.AxisType
==chart2::AxisType::CATEGORY
);
68 m_bIsDateAxis
= (aScale
.AxisType
== chart2::AxisType::DATE
|| m_bIsAutoDate
);
72 if( m_xOriginalCategories
.is() )
74 Reference
< chart2::XChartDocument
> xChartDoc( xChartModel
, uno::UNO_QUERY
);
77 uno::Reference
< data::XDataProvider
> xDataProvider( xChartDoc
->getDataProvider() );
79 OUString
aCategoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories
) );
80 if( xDataProvider
.is() && !aCategoriesRange
.isEmpty() )
82 const bool bFirstCellAsLabel
= false;
83 const bool bHasCategories
= false;
84 const uno::Sequence
< sal_Int32
> aSequenceMapping
;
86 uno::Reference
< data::XDataSource
> xColumnCategoriesSource( xDataProvider
->createDataSource(
87 DataSourceHelper::createArguments( aCategoriesRange
, aSequenceMapping
, true /*bUseColumns*/
88 , bFirstCellAsLabel
, bHasCategories
) ) );
90 uno::Reference
< data::XDataSource
> xRowCategoriesSource( xDataProvider
->createDataSource(
91 DataSourceHelper::createArguments( aCategoriesRange
, aSequenceMapping
, false /*bUseColumns*/
92 , bFirstCellAsLabel
, bHasCategories
) ) );
94 if( xColumnCategoriesSource
.is() && xRowCategoriesSource
.is() )
96 Sequence
< Reference
< data::XLabeledDataSequence
> > aColumns
= xColumnCategoriesSource
->getDataSequences();
97 Sequence
< Reference
< data::XLabeledDataSequence
> > aRows
= xRowCategoriesSource
->getDataSequences();
99 sal_Int32 nColumnCount
= aColumns
.getLength();
100 sal_Int32 nRowCount
= aRows
.getLength();
101 if( nColumnCount
>1 && nRowCount
>1 )
103 //we have complex categories
104 //->split them in the direction of the first series
105 //detect whether the first series is a row or a column
106 bool bSeriesUsesColumns
= true;
107 ::std::vector
< Reference
< XDataSeries
> > aSeries( ChartModelHelper::getDataSeries( xChartModel
) );
108 if( !aSeries
.empty() )
110 uno::Reference
< data::XDataSource
> xSeriesSource( aSeries
.front(), uno::UNO_QUERY
);
111 OUString aStringDummy
;
113 uno::Sequence
< sal_Int32
> aSeqDummy
;
114 DataSourceHelper::readArguments( xDataProvider
->detectArguments( xSeriesSource
),
115 aStringDummy
, aSeqDummy
, bSeriesUsesColumns
, bDummy
, bDummy
);
117 if( bSeriesUsesColumns
)
118 m_aSplitCategoriesList
=aColumns
;
120 m_aSplitCategoriesList
=aRows
;
125 if( !m_aSplitCategoriesList
.getLength() )
127 m_aSplitCategoriesList
.realloc(1);
128 m_aSplitCategoriesList
[0]=m_xOriginalCategories
;
132 catch( const uno::Exception
& ex
)
134 ASSERT_EXCEPTION( ex
);
138 ExplicitCategoriesProvider::~ExplicitCategoriesProvider()
142 Reference
< chart2::data::XDataSequence
> ExplicitCategoriesProvider::getOriginalCategories()
144 if( m_xOriginalCategories
.is() )
145 return m_xOriginalCategories
->getValues();
149 const Sequence
< Reference
< data::XLabeledDataSequence
> >& ExplicitCategoriesProvider::getSplitCategoriesList()
151 return m_aSplitCategoriesList
;
154 bool ExplicitCategoriesProvider::hasComplexCategories() const
156 return m_aSplitCategoriesList
.getLength() > 1;
159 sal_Int32
ExplicitCategoriesProvider::getCategoryLevelCount() const
161 sal_Int32 nCount
= m_aSplitCategoriesList
.getLength();
167 std::vector
<sal_Int32
> lcl_getLimitingBorders( const std::vector
< ComplexCategory
>& rComplexCategories
)
169 std::vector
<sal_Int32
> aLimitingBorders
;
170 std::vector
< ComplexCategory
>::const_iterator
aIt( rComplexCategories
.begin() );
171 std::vector
< ComplexCategory
>::const_iterator
aEnd( rComplexCategories
.end() );
172 sal_Int32 nBorderIndex
= 0; /*border below the index*/
173 for( ; aIt
!= aEnd
; ++aIt
)
175 ComplexCategory
aComplexCategory(*aIt
);
176 nBorderIndex
+= aComplexCategory
.Count
;
177 aLimitingBorders
.push_back(nBorderIndex
);
179 return aLimitingBorders
;
182 void ExplicitCategoriesProvider::convertCategoryAnysToText( uno::Sequence
< OUString
>& rOutTexts
, const uno::Sequence
< uno::Any
>& rInAnys
, Reference
< frame::XModel
> xChartModel
)
184 sal_Int32 nCount
= rInAnys
.getLength();
187 rOutTexts
.realloc(nCount
);
188 Reference
< util::XNumberFormatsSupplier
> xNumberFormatsSupplier( xChartModel
, uno::UNO_QUERY
);
189 Reference
< util::XNumberFormats
> xNumberFormats
;
190 if( xNumberFormatsSupplier
.is() )
191 xNumberFormats
= Reference
< util::XNumberFormats
>( xNumberFormatsSupplier
->getNumberFormats() );
193 sal_Int32 nAxisNumberFormat
= 0;
194 Reference
< XCoordinateSystem
> xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( xChartModel
) );
195 if( xCooSysModel
.is() )
197 Reference
< chart2::XAxis
> xAxis( xCooSysModel
->getAxisByDimension(0,0) );
198 nAxisNumberFormat
= AxisHelper::getExplicitNumberFormatKeyForAxis(
199 xAxis
, xCooSysModel
, xNumberFormatsSupplier
, false );
202 sal_Int32 nLabelColor
;
203 bool bColorChanged
= false;
205 NumberFormatterWrapper
aNumberFormatterWrapper( xNumberFormatsSupplier
);
207 for(sal_Int32 nN
=0;nN
<nCount
;nN
++)
210 uno::Any aAny
= rInAnys
[nN
];
211 if( aAny
.hasValue() )
216 if( !::rtl::math::isNan(fDouble
) )
217 aText
= aNumberFormatterWrapper
.getFormattedString(
218 nAxisNumberFormat
, fDouble
, nLabelColor
, bColorChanged
);
225 rOutTexts
[nN
] = aText
;
229 SplitCategoriesProvider::~SplitCategoriesProvider()
233 class SplitCategoriesProvider_ForLabeledDataSequences
: public SplitCategoriesProvider
237 explicit SplitCategoriesProvider_ForLabeledDataSequences(
238 const ::com::sun::star::uno::Sequence
<
239 ::com::sun::star::uno::Reference
<
240 ::com::sun::star::chart2::data::XLabeledDataSequence
> >& rSplitCategoriesList
241 , const Reference
< frame::XModel
>& xChartModel
)
242 : m_rSplitCategoriesList( rSplitCategoriesList
)
243 , m_xChartModel( xChartModel
)
245 virtual ~SplitCategoriesProvider_ForLabeledDataSequences()
248 virtual sal_Int32
getLevelCount() const;
249 virtual uno::Sequence
< OUString
> getStringsForLevel( sal_Int32 nIndex
) const;
252 const ::com::sun::star::uno::Sequence
< ::com::sun::star::uno::Reference
<
253 ::com::sun::star::chart2::data::XLabeledDataSequence
> >& m_rSplitCategoriesList
;
255 Reference
< frame::XModel
> m_xChartModel
;
258 sal_Int32
SplitCategoriesProvider_ForLabeledDataSequences::getLevelCount() const
260 return m_rSplitCategoriesList
.getLength();
262 uno::Sequence
< OUString
> SplitCategoriesProvider_ForLabeledDataSequences::getStringsForLevel( sal_Int32 nLevel
) const
264 uno::Sequence
< OUString
> aRet
;
265 Reference
< data::XLabeledDataSequence
> xLabeledDataSequence( m_rSplitCategoriesList
[nLevel
] );
266 if( xLabeledDataSequence
.is() )
268 uno::Reference
< data::XDataSequence
> xDataSequence( xLabeledDataSequence
->getValues() );
269 if( xDataSequence
.is() )
270 ExplicitCategoriesProvider::convertCategoryAnysToText( aRet
, xDataSequence
->getData(), m_xChartModel
);
275 std::vector
< ComplexCategory
> lcl_DataSequenceToComplexCategoryVector(
276 const uno::Sequence
< OUString
>& rStrings
277 , const std::vector
<sal_Int32
>& rLimitingBorders
, bool bCreateSingleCategories
)
279 std::vector
< ComplexCategory
> aResult
;
281 sal_Int32 nMaxCount
= rStrings
.getLength();
283 sal_Int32 nCurrentCount
=0;
284 for( sal_Int32 nN
=0; nN
<nMaxCount
; nN
++ )
286 const OUString
& aCurrent
= rStrings
[nN
];
287 if( bCreateSingleCategories
|| ::std::find( rLimitingBorders
.begin(), rLimitingBorders
.end(), nN
) != rLimitingBorders
.end() )
289 aResult
.push_back( ComplexCategory(aPrevious
,nCurrentCount
) );
291 aPrevious
= aCurrent
;
295 // Empty value is interpreted as a continuation of the previous
296 // category. Note that having the same value as the previous one
297 // does not equate to a continuation of the category.
299 if (aCurrent
.isEmpty())
303 aResult
.push_back( ComplexCategory(aPrevious
,nCurrentCount
) );
305 aPrevious
= aCurrent
;
310 aResult
.push_back( ComplexCategory(aPrevious
,nCurrentCount
) );
315 sal_Int32
lcl_getCategoryCount( std::vector
< ComplexCategory
>& rComplexCategories
)
317 sal_Int32 nCount
= 0;
318 std::vector
< ComplexCategory
>::const_iterator
aIt( rComplexCategories
.begin() );
319 std::vector
< ComplexCategory
>::const_iterator
aEnd( rComplexCategories
.end() );
320 for( ; aIt
!= aEnd
; ++aIt
)
325 Sequence
< OUString
> lcl_getExplicitSimpleCategories(
326 const SplitCategoriesProvider
& rSplitCategoriesProvider
,
327 ::std::vector
< ::std::vector
< ComplexCategory
> >& rComplexCats
)
329 Sequence
< OUString
> aRet
;
331 rComplexCats
.clear();
332 sal_Int32 nLCount
= rSplitCategoriesProvider
.getLevelCount();
333 for( sal_Int32 nL
= 0; nL
< nLCount
; nL
++ )
335 std::vector
<sal_Int32
> aLimitingBorders
;
337 aLimitingBorders
= lcl_getLimitingBorders( rComplexCats
.back() );
338 rComplexCats
.push_back( lcl_DataSequenceToComplexCategoryVector(
339 rSplitCategoriesProvider
.getStringsForLevel(nL
), aLimitingBorders
, nL
==(nLCount
-1) ) );
342 std::vector
< std::vector
< ComplexCategory
> >::iterator
aOuterIt( rComplexCats
.begin() );
343 std::vector
< std::vector
< ComplexCategory
> >::const_iterator
aOuterEnd( rComplexCats
.end() );
345 //ensure that the category count is the same on each level
346 sal_Int32 nMaxCategoryCount
= 0;
348 for( aOuterIt
=rComplexCats
.begin(); aOuterIt
!= aOuterEnd
; ++aOuterIt
)
350 sal_Int32 nCurrentCount
= lcl_getCategoryCount( *aOuterIt
);
351 nMaxCategoryCount
= std::max( nCurrentCount
, nMaxCategoryCount
);
353 for( aOuterIt
=rComplexCats
.begin(); aOuterIt
!= aOuterEnd
; ++aOuterIt
)
355 if ( !aOuterIt
->empty() )
357 sal_Int32 nCurrentCount
= lcl_getCategoryCount( *aOuterIt
);
358 if( nCurrentCount
< nMaxCategoryCount
)
360 ComplexCategory
& rComplexCategory
= aOuterIt
->back();
361 rComplexCategory
.Count
+= (nMaxCategoryCount
-nCurrentCount
);
367 //create a list with an element for every index
368 std::vector
< std::vector
< ComplexCategory
> > aComplexCatsPerIndex
;
369 for( aOuterIt
=rComplexCats
.begin() ; aOuterIt
!= aOuterEnd
; ++aOuterIt
)
371 std::vector
< ComplexCategory
> aSingleLevel
;
372 std::vector
< ComplexCategory
>::iterator
aIt( aOuterIt
->begin() );
373 std::vector
< ComplexCategory
>::const_iterator
aEnd( aOuterIt
->end() );
374 for( ; aIt
!= aEnd
; ++aIt
)
376 ComplexCategory
aComplexCategory( *aIt
);
377 sal_Int32 nCount
= aComplexCategory
.Count
;
379 aSingleLevel
.push_back(aComplexCategory
);
381 aComplexCatsPerIndex
.push_back( aSingleLevel
);
384 if(nMaxCategoryCount
)
386 aRet
.realloc(nMaxCategoryCount
);
387 aOuterEnd
= aComplexCatsPerIndex
.end();
388 OUString
aSpace(" ");
389 for(sal_Int32 nN
=0; nN
<nMaxCategoryCount
; nN
++)
392 for( aOuterIt
=aComplexCatsPerIndex
.begin() ; aOuterIt
!= aOuterEnd
; ++aOuterIt
)
394 if ( static_cast<size_t>(nN
) < aOuterIt
->size() )
396 OUString aAddText
= (*aOuterIt
)[nN
].Text
;
397 if( !aAddText
.isEmpty() )
411 Sequence
< OUString
> ExplicitCategoriesProvider::getExplicitSimpleCategories(
412 const SplitCategoriesProvider
& rSplitCategoriesProvider
)
414 vector
< vector
< ComplexCategory
> > aComplexCats
;
415 return lcl_getExplicitSimpleCategories( rSplitCategoriesProvider
, aComplexCats
);
418 struct DatePlusIndexComparator
420 inline bool operator() ( const DatePlusIndex
& aFirst
,
421 const DatePlusIndex
& aSecond
) const
423 return ( aFirst
.fValue
< aSecond
.fValue
);
427 bool lcl_fillDateCategories( const uno::Reference
< data::XDataSequence
>& xDataSequence
, std::vector
< DatePlusIndex
>& rDateCategories
, bool bIsAutoDate
, Reference
< util::XNumberFormatsSupplier
> xNumberFormatsSupplier
)
429 bool bOnlyDatesFound
= true;
430 bool bAnyDataFound
= false;
432 if( xDataSequence
.is() )
434 uno::Sequence
< uno::Any
> aValues
= xDataSequence
->getData();
435 sal_Int32 nCount
= aValues
.getLength();
436 rDateCategories
.reserve(nCount
);
437 Reference
< util::XNumberFormats
> xNumberFormats
;
438 if( xNumberFormatsSupplier
.is() )
439 xNumberFormats
= Reference
< util::XNumberFormats
>( xNumberFormatsSupplier
->getNumberFormats() );
441 bool bOwnData
= false;
442 bool bOwnDataAnddAxisHasAnyFormat
= false;
443 bool bOwnDataAnddAxisHasDateFormat
= false;
444 Reference
< chart2::XChartDocument
> xChartDoc( xNumberFormatsSupplier
, uno::UNO_QUERY
);
445 Reference
< XCoordinateSystem
> xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( Reference
< frame::XModel
>( xChartDoc
, uno::UNO_QUERY
) ) );
446 if( xChartDoc
.is() && xCooSysModel
.is() )
448 if( xChartDoc
->hasInternalDataProvider() )
451 Reference
< beans::XPropertySet
> xAxisProps( xCooSysModel
->getAxisByDimension(0,0), uno::UNO_QUERY
);
452 sal_Int32 nAxisNumberFormat
= 0;
453 if( xAxisProps
.is() && (xAxisProps
->getPropertyValue( "NumberFormat" ) >>= nAxisNumberFormat
) )
455 bOwnDataAnddAxisHasAnyFormat
= true;
456 bOwnDataAnddAxisHasDateFormat
= DiagramHelper::isDateNumberFormat( nAxisNumberFormat
, xNumberFormats
);
461 for(sal_Int32 nN
=0;nN
<nCount
;nN
++)
463 bool bIsDate
= false;
467 bIsDate
= bOwnDataAnddAxisHasAnyFormat
? bOwnDataAnddAxisHasDateFormat
: true;
469 bIsDate
= DiagramHelper::isDateNumberFormat( xDataSequence
->getNumberFormatKeyByIndex( nN
), xNumberFormats
);
474 bool bContainsEmptyString
= false;
475 bool bContainsNan
= false;
476 uno::Any aAny
= aValues
[nN
];
477 if( aAny
.hasValue() )
481 if( (aAny
>>=aTest
) && aTest
.isEmpty() ) //empty String
482 bContainsEmptyString
= true;
483 else if( (aAny
>>=fTest
) && ::rtl::math::isNan(fTest
) )
486 if( !bContainsEmptyString
&& !bContainsNan
)
487 bAnyDataFound
= true;
489 DatePlusIndex
aDatePlusIndex( 1.0, nN
);
490 if( bIsDate
&& (aAny
>>= aDatePlusIndex
.fValue
) )
491 rDateCategories
.push_back( aDatePlusIndex
);
494 if( aAny
.hasValue() && !bContainsEmptyString
)//empty string does not count as non date value!
495 bOnlyDatesFound
=false;
496 ::rtl::math::setNan( &aDatePlusIndex
.fValue
);
497 rDateCategories
.push_back( aDatePlusIndex
);
500 ::std::sort( rDateCategories
.begin(), rDateCategories
.end(), DatePlusIndexComparator() );
503 return bAnyDataFound
&& bOnlyDatesFound
;
506 void ExplicitCategoriesProvider::init()
510 m_aComplexCats
.clear();//not one per index
511 m_aDateCategories
.clear();
513 if( m_xOriginalCategories
.is() )
515 if( !hasComplexCategories() )
519 if( ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( m_xCooSysModel
, 0 ), 2, 0 ) )
520 m_bIsDateAxis
= lcl_fillDateCategories( m_xOriginalCategories
->getValues(), m_aDateCategories
, m_bIsAutoDate
, Reference
< util::XNumberFormatsSupplier
>( m_xChartModel
.get(), uno::UNO_QUERY
) );
522 m_bIsDateAxis
= false;
527 m_bIsDateAxis
= false;
537 Sequence
< OUString
> ExplicitCategoriesProvider::getSimpleCategories()
539 if( !m_bIsExplicitCategoriesInited
)
542 m_aExplicitCategories
.realloc(0);
543 if( m_xOriginalCategories
.is() )
545 if( !hasComplexCategories() )
547 uno::Reference
< data::XDataSequence
> xDataSequence( m_xOriginalCategories
->getValues() );
548 if( xDataSequence
.is() )
549 ExplicitCategoriesProvider::convertCategoryAnysToText( m_aExplicitCategories
, xDataSequence
->getData(), m_xChartModel
);
553 m_aExplicitCategories
= lcl_getExplicitSimpleCategories(
554 SplitCategoriesProvider_ForLabeledDataSequences( m_aSplitCategoriesList
, m_xChartModel
), m_aComplexCats
);
557 if(!m_aExplicitCategories
.getLength())
558 m_aExplicitCategories
= DiagramHelper::generateAutomaticCategoriesFromCooSys( m_xCooSysModel
);
559 m_bIsExplicitCategoriesInited
= true;
561 return m_aExplicitCategories
;
564 const std::vector
<ComplexCategory
>* ExplicitCategoriesProvider::getCategoriesByLevel( sal_Int32 nLevel
)
567 sal_Int32 nMaxIndex
= m_aComplexCats
.size()-1;
568 if (nLevel
>= 0 && nLevel
<= nMaxIndex
)
569 return &m_aComplexCats
[nMaxIndex
-nLevel
];
573 OUString
ExplicitCategoriesProvider::getCategoryByIndex(
574 const Reference
< XCoordinateSystem
>& xCooSysModel
575 , const uno::Reference
< frame::XModel
>& xChartModel
578 if( xCooSysModel
.is())
580 ExplicitCategoriesProvider
aExplicitCategoriesProvider( xCooSysModel
, xChartModel
);
581 Sequence
< OUString
> aCategories( aExplicitCategoriesProvider
.getSimpleCategories());
582 if( nIndex
< aCategories
.getLength())
583 return aCategories
[ nIndex
];
588 bool ExplicitCategoriesProvider::isDateAxis()
591 return m_bIsDateAxis
;
594 const std::vector
< DatePlusIndex
>& ExplicitCategoriesProvider::getDateCategories()
597 return m_aDateCategories
;
600 //.............................................................................
602 //.............................................................................
604 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */