1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_chart2.hxx"
26 
27 #include "ExplicitCategoriesProvider.hxx"
28 #include "DiagramHelper.hxx"
29 #include "ChartTypeHelper.hxx"
30 #include "AxisHelper.hxx"
31 #include "CommonConverters.hxx"
32 #include "DataSourceHelper.hxx"
33 #include "ChartModelHelper.hxx"
34 #include "ContainerHelper.hxx"
35 #include "macros.hxx"
36 #include "NumberFormatterWrapper.hxx"
37 
38 #include <com/sun/star/chart2/AxisType.hpp>
39 #include <com/sun/star/util/NumberFormat.hpp>
40 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
41 
42 //.............................................................................
43 namespace chart
44 {
45 //.............................................................................
46 
47 using namespace ::com::sun::star;
48 using namespace ::com::sun::star::chart2;
49 using ::com::sun::star::uno::Reference;
50 using ::com::sun::star::uno::Sequence;
51 using ::rtl::OUString;
52 using ::std::vector;
53 
54 
55 ExplicitCategoriesProvider::ExplicitCategoriesProvider( const Reference< chart2::XCoordinateSystem >& xCooSysModel
56                                                        , const uno::Reference< frame::XModel >& xChartModel )
57     : m_bDirty(true)
58     , m_xCooSysModel( xCooSysModel )
59     , m_xChartModel( xChartModel )
60     , m_xOriginalCategories()
61     , m_bIsExplicitCategoriesInited(false)
62     , m_bIsDateAxis(false)
63     , m_bIsAutoDate(false)
64 {
65     try
66     {
67         if( xCooSysModel.is() )
68         {
69             uno::Reference< XAxis > xAxis( xCooSysModel->getAxisByDimension(0,0) );
70             if( xAxis.is() )
71             {
72                 ScaleData aScale( xAxis->getScaleData() );
73                 m_xOriginalCategories = aScale.Categories;
74                 m_bIsAutoDate = (aScale.AutoDateAxis && aScale.AxisType==chart2::AxisType::CATEGORY);
75                 m_bIsDateAxis = (aScale.AxisType == chart2::AxisType::DATE || m_bIsAutoDate);
76             }
77         }
78 
79         if( m_xOriginalCategories.is() )
80         {
81             Reference< chart2::XChartDocument > xChartDoc( xChartModel, uno::UNO_QUERY );
82             if( xChartDoc.is() )
83             {
84                 uno::Reference< data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
85 
86                 OUString aCatgoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories ) );
87                 if( xDataProvider.is() && aCatgoriesRange.getLength() )
88                 {
89                     const bool bFirstCellAsLabel = false;
90                     const bool bHasCategories = false;
91                     const uno::Sequence< sal_Int32 > aSequenceMapping;
92 
93                     uno::Reference< data::XDataSource > xColumnCategoriesSource( xDataProvider->createDataSource(
94                          DataSourceHelper::createArguments( aCatgoriesRange, aSequenceMapping, true /*bUseColumns*/
95                             , bFirstCellAsLabel, bHasCategories ) ) );
96 
97                     uno::Reference< data::XDataSource > xRowCategoriesSource( xDataProvider->createDataSource(
98                          DataSourceHelper::createArguments( aCatgoriesRange, aSequenceMapping, false /*bUseColumns*/
99                             , bFirstCellAsLabel, bHasCategories ) ) );
100 
101                     if( xColumnCategoriesSource.is() &&  xRowCategoriesSource.is() )
102                     {
103                         Sequence< Reference< data::XLabeledDataSequence> > aColumns = xColumnCategoriesSource->getDataSequences();
104                         Sequence< Reference< data::XLabeledDataSequence> > aRows = xRowCategoriesSource->getDataSequences();
105 
106                         sal_Int32 nColumnCount = aColumns.getLength();
107                         sal_Int32 nRowCount = aRows.getLength();
108                         if( nColumnCount>1 && nRowCount>1 )
109                         {
110                             //we have complex categories
111                             //->split them in the direction of the first series
112                             //detect whether the first series is a row or a column
113                             bool bSeriesUsesColumns = true;
114                             ::std::vector< Reference< XDataSeries > > aSeries( ChartModelHelper::getDataSeries( xChartModel ) );
115                             if( !aSeries.empty() )
116                             {
117                                 uno::Reference< data::XDataSource > xSeriesSource( aSeries.front(), uno::UNO_QUERY );
118                                 ::rtl::OUString aStringDummy;
119                                 bool bDummy;
120                                 uno::Sequence< sal_Int32 > aSeqDummy;
121                                 DataSourceHelper::readArguments( xDataProvider->detectArguments( xSeriesSource),
122                                                aStringDummy, aSeqDummy, bSeriesUsesColumns, bDummy, bDummy );
123                             }
124                             if( bSeriesUsesColumns )
125                                 m_aSplitCategoriesList=aColumns;
126                             else
127                                 m_aSplitCategoriesList=aRows;
128                         }
129                     }
130                 }
131             }
132             if( !m_aSplitCategoriesList.getLength() )
133             {
134                 m_aSplitCategoriesList.realloc(1);
135                 m_aSplitCategoriesList[0]=m_xOriginalCategories;
136             }
137         }
138     }
139     catch( const uno::Exception & ex )
140     {
141         ASSERT_EXCEPTION( ex );
142     }
143 }
144 
145 ExplicitCategoriesProvider::~ExplicitCategoriesProvider()
146 {
147 }
148 
149 Reference< chart2::data::XDataSequence > ExplicitCategoriesProvider::getOriginalCategories()
150 {
151     if( m_xOriginalCategories.is() )
152         return m_xOriginalCategories->getValues();
153     return 0;
154 }
155 
156 const Sequence< Reference< data::XLabeledDataSequence> >& ExplicitCategoriesProvider::getSplitCategoriesList()
157 {
158     return m_aSplitCategoriesList;
159 }
160 
161 bool ExplicitCategoriesProvider::hasComplexCategories() const
162 {
163     return m_aSplitCategoriesList.getLength() > 1;
164 }
165 
166 sal_Int32 ExplicitCategoriesProvider::getCategoryLevelCount() const
167 {
168     sal_Int32 nCount = m_aSplitCategoriesList.getLength();
169     if(!nCount)
170         nCount = 1;
171     return nCount;
172 }
173 
174 std::vector<sal_Int32> lcl_getLimitingBorders( const std::vector< ComplexCategory >& rComplexCategories )
175 {
176     std::vector<sal_Int32> aLimitingBorders;
177     std::vector< ComplexCategory >::const_iterator aIt( rComplexCategories.begin() );
178     std::vector< ComplexCategory >::const_iterator aEnd( rComplexCategories.end() );
179     sal_Int32 nBorderIndex = 0; /*border below the index*/
180     for( ; aIt != aEnd; ++aIt )
181     {
182         ComplexCategory aComplexCategory(*aIt);
183         nBorderIndex += aComplexCategory.Count;
184         aLimitingBorders.push_back(nBorderIndex);
185     }
186     return aLimitingBorders;
187 }
188 
189 void ExplicitCategoriesProvider::convertCategoryAnysToText( uno::Sequence< rtl::OUString >& rOutTexts, const uno::Sequence< uno::Any >& rInAnys, Reference< frame::XModel > xChartModel )
190 {
191     sal_Int32 nCount = rInAnys.getLength();
192     if(!nCount)
193         return;
194     rOutTexts.realloc(nCount);
195     Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( xChartModel, uno::UNO_QUERY );
196     Reference< util::XNumberFormats > xNumberFormats;
197     if( xNumberFormatsSupplier.is() )
198          xNumberFormats = Reference< util::XNumberFormats >( xNumberFormatsSupplier->getNumberFormats() );
199 
200     sal_Int32 nAxisNumberFormat = 0;
201     Reference< XCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( xChartModel ) );
202     if( xCooSysModel.is() )
203     {
204         Reference< chart2::XAxis > xAxis( xCooSysModel->getAxisByDimension(0,0) );
205         nAxisNumberFormat = AxisHelper::getExplicitNumberFormatKeyForAxis(
206                   xAxis, xCooSysModel, xNumberFormatsSupplier, false );
207     }
208 
209     sal_Int32 nLabelColor;
210     bool bColorChanged = false;
211 
212     NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier );
213 
214     for(sal_Int32 nN=0;nN<nCount;nN++)
215     {
216         rtl::OUString aText;
217         uno::Any aAny = rInAnys[nN];
218         if( aAny.hasValue() )
219         {
220             double fDouble = 0;
221             if( aAny>>=fDouble )
222             {
223                 if( !::rtl::math::isNan(fDouble) )
224                     aText = aNumberFormatterWrapper.getFormattedString(
225                         nAxisNumberFormat, fDouble, nLabelColor, bColorChanged );
226             }
227             else
228             {
229                 aAny>>=aText;
230             }
231         }
232         rOutTexts[nN] = aText;
233     }
234 }
235 
236 SplitCategoriesProvider::~SplitCategoriesProvider()
237 {
238 }
239 
240 class SplitCategoriesProvider_ForLabeledDataSequences : public SplitCategoriesProvider
241 {
242 public:
243 
244     explicit SplitCategoriesProvider_ForLabeledDataSequences(
245         const ::com::sun::star::uno::Sequence<
246             ::com::sun::star::uno::Reference<
247                 ::com::sun::star::chart2::data::XLabeledDataSequence> >& rSplitCategoriesList
248         , const Reference< frame::XModel >& xChartModel )
249         : m_rSplitCategoriesList( rSplitCategoriesList )
250         , m_xChartModel( xChartModel )
251     {}
252     virtual ~SplitCategoriesProvider_ForLabeledDataSequences()
253     {}
254 
255     virtual sal_Int32 getLevelCount() const;
256     virtual uno::Sequence< rtl::OUString > getStringsForLevel( sal_Int32 nIndex ) const;
257 
258 private:
259     const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference<
260         ::com::sun::star::chart2::data::XLabeledDataSequence> >& m_rSplitCategoriesList;
261 
262     Reference< frame::XModel > m_xChartModel;
263 };
264 
265 sal_Int32 SplitCategoriesProvider_ForLabeledDataSequences::getLevelCount() const
266 {
267     return m_rSplitCategoriesList.getLength();
268 }
269 uno::Sequence< rtl::OUString > SplitCategoriesProvider_ForLabeledDataSequences::getStringsForLevel( sal_Int32 nLevel ) const
270 {
271     uno::Sequence< rtl::OUString > aRet;
272     Reference< data::XLabeledDataSequence > xLabeledDataSequence( m_rSplitCategoriesList[nLevel] );
273     if( xLabeledDataSequence.is() )
274     {
275         uno::Reference< data::XDataSequence > xDataSequence( xLabeledDataSequence->getValues() );
276         if( xDataSequence.is() )
277             ExplicitCategoriesProvider::convertCategoryAnysToText( aRet, xDataSequence->getData(), m_xChartModel );
278     }
279     return aRet;
280 }
281 
282 std::vector< ComplexCategory > lcl_DataSequenceToComplexCategoryVector(
283     const uno::Sequence< rtl::OUString >& rStrings
284     , const std::vector<sal_Int32>& rLimitingBorders, bool bCreateSingleCategories )
285 {
286     std::vector< ComplexCategory > aResult;
287 
288     sal_Int32 nMaxCount = rStrings.getLength();
289     OUString aPrevious;
290     sal_Int32 nCurrentCount=0;
291     for( sal_Int32 nN=0; nN<nMaxCount; nN++ )
292     {
293         OUString aCurrent = rStrings[nN];
294         if( bCreateSingleCategories || ::std::find( rLimitingBorders.begin(), rLimitingBorders.end(), nN ) != rLimitingBorders.end() )
295         {
296             aResult.push_back( ComplexCategory(aPrevious,nCurrentCount) );
297             nCurrentCount=1;
298             aPrevious = aCurrent;
299         }
300         else
301         {
302             if( aCurrent.getLength() && aPrevious != aCurrent )
303             {
304                 aResult.push_back( ComplexCategory(aPrevious,nCurrentCount) );
305                 nCurrentCount=1;
306                 aPrevious = aCurrent;
307             }
308             else
309                 nCurrentCount++;
310         }
311     }
312     if( nCurrentCount )
313         aResult.push_back( ComplexCategory(aPrevious,nCurrentCount) );
314 
315     return aResult;
316 }
317 
318 sal_Int32 lcl_getCategoryCount( std::vector< ComplexCategory >& rComplexCategories )
319 {
320     sal_Int32 nCount = 0;
321     std::vector< ComplexCategory >::iterator aIt( rComplexCategories.begin() );
322     std::vector< ComplexCategory >::const_iterator aEnd( rComplexCategories.end() );
323     for( ; aIt != aEnd; ++aIt )
324         nCount+=aIt->Count;
325     return nCount;
326 }
327 
328 Sequence< OUString > lcl_getExplicitSimpleCategories(
329     const SplitCategoriesProvider& rSplitCategoriesProvider,
330     ::std::vector< ::std::vector< ComplexCategory > >& rComplexCats )
331 {
332     Sequence< OUString > aRet;
333 
334     rComplexCats.clear();
335     sal_Int32 nLCount = rSplitCategoriesProvider.getLevelCount();
336     for( sal_Int32 nL = 0; nL < nLCount; nL++ )
337     {
338         std::vector<sal_Int32> aLimitingBorders;
339         if(nL>0)
340             aLimitingBorders = lcl_getLimitingBorders( rComplexCats.back() );
341         rComplexCats.push_back( lcl_DataSequenceToComplexCategoryVector(
342             rSplitCategoriesProvider.getStringsForLevel(nL), aLimitingBorders, nL==(nLCount-1) ) );
343     }
344 
345     std::vector< std::vector< ComplexCategory > >::iterator aOuterIt( rComplexCats.begin() );
346     std::vector< std::vector< ComplexCategory > >::const_iterator aOuterEnd( rComplexCats.end() );
347 
348     //ensure that the category count is the same on each level
349     sal_Int32 nMaxCategoryCount = 0;
350     {
351         for( aOuterIt=rComplexCats.begin(); aOuterIt != aOuterEnd; ++aOuterIt )
352         {
353             sal_Int32 nCurrentCount = lcl_getCategoryCount( *aOuterIt );
354             nMaxCategoryCount = std::max( nCurrentCount, nMaxCategoryCount );
355         }
356         for( aOuterIt=rComplexCats.begin(); aOuterIt != aOuterEnd; ++aOuterIt )
357         {
358             sal_Int32 nCurrentCount = lcl_getCategoryCount( *aOuterIt );
359             if( nCurrentCount< nMaxCategoryCount )
360             {
361                 ComplexCategory& rComplexCategory = aOuterIt->back();
362                 rComplexCategory.Count += (nMaxCategoryCount-nCurrentCount);
363             }
364         }
365     }
366 
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 )
370     {
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 )
375         {
376             ComplexCategory aComplexCategory( *aIt );
377             sal_Int32 nCount = aComplexCategory.Count;
378             while( nCount-- )
379                 aSingleLevel.push_back(aComplexCategory);
380         }
381         aComplexCatsPerIndex.push_back( aSingleLevel );
382     }
383 
384     if(nMaxCategoryCount)
385     {
386         aRet.realloc(nMaxCategoryCount);
387         aOuterEnd = aComplexCatsPerIndex.end();
388         OUString aSpace(C2U(" "));
389         for(sal_Int32 nN=0; nN<nMaxCategoryCount; nN++)
390         {
391             OUString aText;
392             for( aOuterIt=aComplexCatsPerIndex.begin() ; aOuterIt != aOuterEnd; ++aOuterIt )
393             {
394                 OUString aAddText = (*aOuterIt)[nN].Text;
395                 if( aAddText.getLength() )
396                 {
397                     if(aText.getLength())
398                         aText += aSpace;
399                     aText += aAddText;
400                 }
401             }
402             aRet[nN]=aText;
403         }
404     }
405     return aRet;
406 }
407 
408 Sequence< OUString > ExplicitCategoriesProvider::getExplicitSimpleCategories(
409     const SplitCategoriesProvider& rSplitCategoriesProvider )
410 {
411     vector< vector< ComplexCategory > > aComplexCats;
412     return lcl_getExplicitSimpleCategories( rSplitCategoriesProvider, aComplexCats );
413 }
414 
415 struct DatePlusIndexComparator
416 {
417     inline bool operator() ( const DatePlusIndex& aFirst,
418                              const DatePlusIndex& aSecond )
419     {
420         return ( aFirst.fValue < aSecond.fValue );
421     }
422 };
423 
424 bool lcl_fillDateCategories( const uno::Reference< data::XDataSequence >& xDataSequence, std::vector< DatePlusIndex >& rDateCategories, bool bIsAutoDate, Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier )
425 {
426     bool bOnlyDatesFound = true;
427     bool bAnyDataFound = false;
428 
429     if( xDataSequence.is() )
430     {
431         uno::Sequence< uno::Any > aValues = xDataSequence->getData();
432         sal_Int32 nCount = aValues.getLength();
433         rDateCategories.reserve(nCount);
434         Reference< util::XNumberFormats > xNumberFormats;
435         if( xNumberFormatsSupplier.is() )
436              xNumberFormats = Reference< util::XNumberFormats >( xNumberFormatsSupplier->getNumberFormats() );
437 
438         bool bOwnData = false;
439         bool bOwnDataAnddAxisHasAnyFormat = false;
440         bool bOwnDataAnddAxisHasDateFormat = false;
441         Reference< chart2::XChartDocument > xChartDoc( xNumberFormatsSupplier, uno::UNO_QUERY );
442         Reference< XCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( Reference< frame::XModel >( xChartDoc, uno::UNO_QUERY ) ) );
443         if( xChartDoc.is() && xCooSysModel.is() )
444         {
445             if( xChartDoc->hasInternalDataProvider() )
446             {
447                 bOwnData = true;
448                 Reference< beans::XPropertySet > xAxisProps( xCooSysModel->getAxisByDimension(0,0), uno::UNO_QUERY );
449                 sal_Int32 nAxisNumberFormat = 0;
450                 if( xAxisProps.is() && (xAxisProps->getPropertyValue( C2U("NumberFormat") ) >>= nAxisNumberFormat) )
451                 {
452                     bOwnDataAnddAxisHasAnyFormat = true;
453                     bOwnDataAnddAxisHasDateFormat = DiagramHelper::isDateNumberFormat( nAxisNumberFormat, xNumberFormats );
454                 }
455             }
456         }
457 
458         for(sal_Int32 nN=0;nN<nCount;nN++)
459         {
460             bool bIsDate = false;
461             if( bIsAutoDate )
462             {
463                 if( bOwnData )
464                     bIsDate = bOwnDataAnddAxisHasAnyFormat ? bOwnDataAnddAxisHasDateFormat : true;
465                 else
466                     bIsDate = DiagramHelper::isDateNumberFormat( xDataSequence->getNumberFormatKeyByIndex( nN ), xNumberFormats );
467             }
468             else
469                 bIsDate = true;
470 
471             bool bContainsEmptyString = false;
472             bool bContainsNan = false;
473             uno::Any aAny = aValues[nN];
474             if( aAny.hasValue() )
475             {
476                 OUString aTest;
477                 double fTest = 0;
478                 if( (aAny>>=aTest) && !aTest.getLength() ) //empty String
479                     bContainsEmptyString = true;
480                 else if( (aAny>>=fTest) &&  ::rtl::math::isNan(fTest) )
481                     bContainsNan = true;
482 
483                 if( !bContainsEmptyString && !bContainsNan )
484                     bAnyDataFound = true;
485             }
486             DatePlusIndex aDatePlusIndex( 1.0, nN );
487             if( bIsDate && (aAny >>= aDatePlusIndex.fValue) )
488                 rDateCategories.push_back( aDatePlusIndex );
489             else
490             {
491                 if( aAny.hasValue() && !bContainsEmptyString )//empty string does not count as non date value!
492                     bOnlyDatesFound=false;
493                 ::rtl::math::setNan( &aDatePlusIndex.fValue );
494                 rDateCategories.push_back( aDatePlusIndex );
495             }
496         }
497         ::std::sort( rDateCategories.begin(), rDateCategories.end(), DatePlusIndexComparator() );
498     }
499 
500     return bAnyDataFound && bOnlyDatesFound;
501 }
502 
503 void ExplicitCategoriesProvider::init()
504 {
505     if( m_bDirty )
506     {
507         m_aComplexCats.clear();//not one per index
508         m_aDateCategories.clear();
509 
510         if( m_xOriginalCategories.is() )
511         {
512             if( !hasComplexCategories() )
513             {
514                 if(m_bIsDateAxis)
515                 {
516                     if( ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( m_xCooSysModel, 0 ), 2, 0 ) )
517                         m_bIsDateAxis = lcl_fillDateCategories( m_xOriginalCategories->getValues(), m_aDateCategories, m_bIsAutoDate, Reference< util::XNumberFormatsSupplier >( m_xChartModel.get(), uno::UNO_QUERY ) );
518                     else
519                         m_bIsDateAxis = false;
520                 }
521             }
522             else
523             {
524                 m_bIsDateAxis = false;
525             }
526         }
527         else
528             m_bIsDateAxis=false;
529         m_bDirty = false;
530     }
531 }
532 
533 
534 Sequence< ::rtl::OUString > ExplicitCategoriesProvider::getSimpleCategories()
535 {
536     if( !m_bIsExplicitCategoriesInited )
537     {
538         init();
539         m_aExplicitCategories.realloc(0);
540         if( m_xOriginalCategories.is() )
541         {
542             if( !hasComplexCategories() )
543             {
544                 uno::Reference< data::XDataSequence > xDataSequence( m_xOriginalCategories->getValues() );
545                 if( xDataSequence.is() )
546                     ExplicitCategoriesProvider::convertCategoryAnysToText( m_aExplicitCategories, xDataSequence->getData(), m_xChartModel );
547             }
548             else
549             {
550                 m_aExplicitCategories = lcl_getExplicitSimpleCategories(
551                     SplitCategoriesProvider_ForLabeledDataSequences( m_aSplitCategoriesList, m_xChartModel ), m_aComplexCats );
552             }
553         }
554         if(!m_aExplicitCategories.getLength())
555             m_aExplicitCategories = DiagramHelper::generateAutomaticCategoriesFromCooSys( m_xCooSysModel );
556         m_bIsExplicitCategoriesInited = true;
557     }
558     return m_aExplicitCategories;
559 }
560 
561 std::vector< ComplexCategory >  ExplicitCategoriesProvider::getCategoriesByLevel( sal_Int32 nLevel )
562 {
563     std::vector< ComplexCategory > aRet;
564     init();
565     sal_Int32 nMaxIndex = m_aComplexCats.size()-1;
566     if( nLevel >= 0 && nLevel <= nMaxIndex  )
567         aRet = m_aComplexCats[nMaxIndex-nLevel];
568     return aRet;
569 }
570 
571 OUString ExplicitCategoriesProvider::getCategoryByIndex(
572           const Reference< XCoordinateSystem >& xCooSysModel
573         , const uno::Reference< frame::XModel >& xChartModel
574         , sal_Int32 nIndex )
575 {
576     if( xCooSysModel.is())
577     {
578         ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSysModel, xChartModel );
579         Sequence< OUString > aCategories( aExplicitCategoriesProvider.getSimpleCategories());
580         if( nIndex < aCategories.getLength())
581             return aCategories[ nIndex ];
582     }
583     return OUString();
584 }
585 
586 bool ExplicitCategoriesProvider::isDateAxis()
587 {
588     init();
589     return m_bIsDateAxis;
590 }
591 
592 const std::vector< DatePlusIndex >&  ExplicitCategoriesProvider::getDateCategories()
593 {
594     init();
595     return m_aDateCategories;
596 }
597 
598 //.............................................................................
599 } //namespace chart
600 //.............................................................................
601