Avoid potential negative array index access to cached text.
[LibreOffice.git] / chart2 / source / tools / DataSeriesHelper.cxx
blobde88c37ecc96d09368a7f54f10ecbbab00a567df
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 <DataSeriesHelper.hxx>
21 #include <DataSeries.hxx>
22 #include <DataSeriesProperties.hxx>
23 #include <DataSource.hxx>
24 #include <ChartType.hxx>
25 #include <unonames.hxx>
26 #include <Diagram.hxx>
27 #include <BaseCoordinateSystem.hxx>
28 #include <Axis.hxx>
30 #include <com/sun/star/beans/XPropertySet.hpp>
31 #include <com/sun/star/chart2/DataPointLabel.hpp>
32 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
33 #include <com/sun/star/chart2/StackingDirection.hpp>
34 #include <com/sun/star/chart2/data/LabelOrigin.hpp>
35 #include <com/sun/star/chart2/AxisType.hpp>
36 #include <com/sun/star/chart2/SymbolStyle.hpp>
37 #include <com/sun/star/chart2/Symbol.hpp>
38 #include <com/sun/star/chart2/XDiagram.hpp>
39 #include <com/sun/star/drawing/LineStyle.hpp>
41 #include <comphelper/sequence.hxx>
42 #include <rtl/ustrbuf.hxx>
43 #include <comphelper/diagnose_ex.hxx>
45 #include <algorithm>
46 #include <iterator>
47 #include <utility>
48 #include <vector>
49 #include <set>
51 using namespace ::com::sun::star;
52 using namespace ::com::sun::star::chart2;
53 using namespace ::chart::DataSeriesProperties;
55 using ::com::sun::star::uno::Reference;
56 using ::com::sun::star::uno::Sequence;
58 namespace
61 class lcl_MatchesRole
63 public:
64 explicit lcl_MatchesRole( OUString aRole, bool bMatchPrefix ) :
65 m_aRole(std::move( aRole )),
66 m_bMatchPrefix( bMatchPrefix )
69 bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
71 if(!xSeq.is())
72 return false;
73 Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
74 OUString aRole;
76 if( m_bMatchPrefix )
77 return ( xProp.is() &&
78 (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
79 aRole.match( m_aRole ));
81 return ( xProp.is() &&
82 (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
83 m_aRole == aRole );
86 private:
87 OUString m_aRole;
88 bool m_bMatchPrefix;
91 void lcl_getCooSysAndChartTypeOfSeries(
92 const rtl::Reference< ::chart::DataSeries > & xSeries,
93 const Reference< chart2::XDiagram > & xDiagram,
94 rtl::Reference< ::chart::BaseCoordinateSystem > & xOutCooSys,
95 rtl::Reference< ::chart::ChartType > & xOutChartType )
97 if( !xDiagram.is())
98 return;
99 ::chart::Diagram* pDiagram = dynamic_cast<::chart::Diagram*>(xDiagram.get());
101 for( rtl::Reference< ::chart::BaseCoordinateSystem > const & coords : pDiagram->getBaseCoordinateSystems() )
103 for( rtl::Reference< ::chart::ChartType > const & chartType : coords->getChartTypes2() )
105 for( rtl::Reference< ::chart::DataSeries > const & dataSeries : chartType->getDataSeries2() )
107 if( dataSeries == xSeries )
109 xOutCooSys = coords;
110 xOutChartType = chartType;
117 void lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( const rtl::Reference< ::chart::DataSeries >& xSeries, bool bInsert )
121 if( xSeries.is() )
123 DataPointLabel aLabelAtSeries;
124 xSeries->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabelAtSeries;
125 aLabelAtSeries.ShowNumber = bInsert;
126 if( !bInsert )
128 aLabelAtSeries.ShowNumberInPercent = false;
129 aLabelAtSeries.ShowCategoryName = false;
131 xSeries->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabelAtSeries));
132 uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
133 // "AttributedDataPoints"
134 if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList )
136 for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
138 Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
139 if( xPointProp.is() )
141 DataPointLabel aLabel;
142 xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel;
143 aLabel.ShowNumber = bInsert;
144 if( !bInsert )
146 aLabel.ShowNumberInPercent = false;
147 aLabel.ShowCategoryName = false;
148 aLabel.ShowCustomLabel = false;
149 aLabel.ShowSeriesName = false;
151 xPointProp->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel));
152 xPointProp->setPropertyValue(CHART_UNONAME_CUSTOM_LABEL_FIELDS, uno::Any());
158 catch(const uno::Exception &)
160 TOOLS_WARN_EXCEPTION("chart2", "" );
164 } // anonymous namespace
166 namespace chart::DataSeriesHelper
169 OUString getRole( const uno::Reference< chart2::data::XLabeledDataSequence >& xLabeledDataSequence )
171 OUString aRet;
172 if( xLabeledDataSequence.is() )
174 Reference< beans::XPropertySet > xProp( xLabeledDataSequence->getValues(), uno::UNO_QUERY );
175 if( xProp.is() )
176 xProp->getPropertyValue( "Role" ) >>= aRet;
178 return aRet;
181 uno::Reference< chart2::data::XLabeledDataSequence >
182 getDataSequenceByRole(
183 const Reference< chart2::data::XDataSource > & xSource,
184 const OUString& aRole,
185 bool bMatchPrefix /* = false */ )
187 uno::Reference< chart2::data::XLabeledDataSequence > aNoResult;
188 if( ! xSource.is())
189 return aNoResult;
190 const Sequence< Reference< chart2::data::XLabeledDataSequence > > aLabeledSeq( xSource->getDataSequences());
193 for (auto const & i : aLabeledSeq)
195 if (lcl_MatchesRole(aRole, bMatchPrefix)(i))
196 return i;
199 catch (const lang::DisposedException&)
201 TOOLS_WARN_EXCEPTION( "chart2", "unexpected exception caught" );
204 return aNoResult;
207 std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >
208 getAllDataSequencesByRole( const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aDataSequences,
209 const OUString& aRole )
211 std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aResultVec;
212 for (const auto & i : aDataSequences)
214 if (lcl_MatchesRole(aRole, /*bMatchPrefix*/true)(i))
215 aResultVec.push_back(i);
217 return aResultVec;
220 std::vector< css::uno::Reference< css::chart2::data::XLabeledDataSequence > >
221 getAllDataSequencesByRole( const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aDataSequences,
222 const OUString& aRole )
224 std::vector< css::uno::Reference< css::chart2::data::XLabeledDataSequence > > aResultVec;
225 std::copy_if( aDataSequences.begin(), aDataSequences.end(),
226 std::back_inserter( aResultVec ),
227 lcl_MatchesRole(aRole, /*bMatchPrefix*/true) );
228 return aResultVec;
231 std::vector<uno::Reference<chart2::data::XLabeledDataSequence> >
232 getAllDataSequences( const std::vector<rtl::Reference<DataSeries> >& aSeries )
234 std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVec;
236 for( rtl::Reference<DataSeries> const & dataSeries : aSeries )
238 const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aSeq( dataSeries->getDataSequences2());
239 aSeqVec.insert( aSeqVec.end(), aSeq.begin(), aSeq.end() );
242 return aSeqVec;
245 rtl::Reference< DataSource >
246 getDataSource( const std::vector< rtl::Reference< DataSeries > > & aSeries )
248 return new DataSource(getAllDataSequences(aSeries));
251 void setStackModeAtSeries(
252 const std::vector< rtl::Reference< DataSeries > > & aSeries,
253 const rtl::Reference< BaseCoordinateSystem > & xCorrespondingCoordinateSystem,
254 StackMode eStackMode )
256 const uno::Any aPropValue(
257 ( (eStackMode == StackMode::YStacked) ||
258 (eStackMode == StackMode::YStackedPercent) )
259 ? chart2::StackingDirection_Y_STACKING
260 : (eStackMode == StackMode::ZStacked )
261 ? chart2::StackingDirection_Z_STACKING
262 : chart2::StackingDirection_NO_STACKING );
264 std::set< sal_Int32 > aAxisIndexSet;
265 for( rtl::Reference< DataSeries > const & dataSeries : aSeries )
269 if( dataSeries.is() )
271 dataSeries->setPropertyValue( "StackingDirection", aPropValue );
273 sal_Int32 nAxisIndex = 0;
274 dataSeries->getPropertyValue( "AttachedAxisIndex" ) >>= nAxisIndex;
275 aAxisIndexSet.insert(nAxisIndex);
278 catch( const uno::Exception & )
280 DBG_UNHANDLED_EXCEPTION("chart2");
284 if( !(xCorrespondingCoordinateSystem.is() &&
285 1 < xCorrespondingCoordinateSystem->getDimension()) )
286 return;
288 if( aAxisIndexSet.empty() )
290 aAxisIndexSet.insert(0);
293 for (auto const& axisIndex : aAxisIndexSet)
295 rtl::Reference< Axis > xAxis =
296 xCorrespondingCoordinateSystem->getAxisByDimension2(1, axisIndex);
297 if( xAxis.is())
299 bool bPercent = (eStackMode == StackMode::YStackedPercent);
300 chart2::ScaleData aScaleData = xAxis->getScaleData();
302 if( bPercent != (aScaleData.AxisType==chart2::AxisType::PERCENT) )
304 if( bPercent )
305 aScaleData.AxisType = chart2::AxisType::PERCENT;
306 else
307 aScaleData.AxisType = chart2::AxisType::REALNUMBER;
308 xAxis->setScaleData( aScaleData );
314 sal_Int32 getAttachedAxisIndex( const rtl::Reference< DataSeries > & xSeries )
316 sal_Int32 nRet = 0;
319 if( xSeries.is() )
321 xSeries->getPropertyValue( "AttachedAxisIndex" ) >>= nRet;
324 catch( const uno::Exception & )
326 DBG_UNHANDLED_EXCEPTION("chart2");
328 return nRet;
331 sal_Int32 getNumberFormatKeyFromAxis(
332 const rtl::Reference< DataSeries > & xSeries,
333 const rtl::Reference< BaseCoordinateSystem > & xCorrespondingCoordinateSystem,
334 sal_Int32 nDimensionIndex,
335 sal_Int32 nAxisIndex /* = -1 */ )
337 sal_Int32 nResult = 0;
338 if( nAxisIndex == -1 )
339 nAxisIndex = getAttachedAxisIndex( xSeries );
342 rtl::Reference< Axis > xAxisProp =
343 xCorrespondingCoordinateSystem->getAxisByDimension2( nDimensionIndex, nAxisIndex );
344 if( xAxisProp.is())
345 xAxisProp->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nResult;
347 catch( const uno::Exception & )
349 DBG_UNHANDLED_EXCEPTION("chart2");
352 return nResult;
355 rtl::Reference< ::chart::BaseCoordinateSystem > getCoordinateSystemOfSeries(
356 const rtl::Reference< DataSeries > & xSeries,
357 const rtl::Reference< Diagram > & xDiagram )
359 rtl::Reference< ::chart::BaseCoordinateSystem > xResult;
360 rtl::Reference< ::chart::ChartType > xDummy;
361 lcl_getCooSysAndChartTypeOfSeries( xSeries, xDiagram, xResult, xDummy );
363 return xResult;
366 rtl::Reference< ::chart::ChartType > getChartTypeOfSeries(
367 const rtl::Reference< DataSeries > & xSeries,
368 const rtl::Reference< Diagram > & xDiagram )
370 rtl::Reference< ::chart::ChartType > xResult;
371 rtl::Reference< ::chart::BaseCoordinateSystem > xDummy;
372 lcl_getCooSysAndChartTypeOfSeries( xSeries, xDiagram, xDummy, xResult );
374 return xResult;
377 void deleteSeries(
378 const rtl::Reference< ::chart::DataSeries > & xSeries,
379 const rtl::Reference< ::chart::ChartType > & xChartType )
383 std::vector< rtl::Reference< DataSeries > > aSeries = xChartType->getDataSeries2();
384 auto aIt = std::find( aSeries.begin(), aSeries.end(), xSeries );
385 if( aIt != aSeries.end())
387 aSeries.erase( aIt );
388 xChartType->setDataSeries( aSeries );
391 catch( const uno::Exception & )
393 DBG_UNHANDLED_EXCEPTION("chart2");
397 void switchSymbolsOnOrOff( const rtl::Reference< DataSeries > & xSeries,
398 bool bSymbolsOn, sal_Int32 nSeriesIndex )
400 if( !xSeries )
401 return;
403 chart2::Symbol aSymbProp;
404 if( xSeries->getPropertyValue( "Symbol") >>= aSymbProp )
406 if( !bSymbolsOn )
407 aSymbProp.Style = chart2::SymbolStyle_NONE;
408 else if( aSymbProp.Style == chart2::SymbolStyle_NONE )
410 aSymbProp.Style = chart2::SymbolStyle_STANDARD;
411 aSymbProp.StandardSymbol = nSeriesIndex;
413 xSeries->setPropertyValue( "Symbol", uno::Any( aSymbProp ));
415 //todo: check attributed data points
418 void switchLinesOnOrOff( const rtl::Reference< DataSeries > & xSeries, bool bLinesOn )
420 if( !xSeries )
421 return;
423 if( bLinesOn )
425 // keep line-styles that are not NONE
426 drawing::LineStyle eLineStyle;
427 if( (xSeries->getPropertyValue( "LineStyle") >>= eLineStyle ) &&
428 eLineStyle == drawing::LineStyle_NONE )
430 xSeries->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_SOLID ) );
433 else
434 xSeries->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_NONE ) );
437 void makeLinesThickOrThin( const rtl::Reference< ::chart::DataSeries > & xSeries, bool bThick )
439 if( !xSeries )
440 return;
442 sal_Int32 nNewValue = bThick ? 80 : 0;
443 sal_Int32 nOldValue = 0;
444 if( (xSeries->getPropertyValue( "LineWidth") >>= nOldValue ) &&
445 nOldValue != nNewValue )
447 if( !(bThick && nOldValue>0))
448 xSeries->setPropertyValue( "LineWidth", uno::Any( nNewValue ) );
452 void setPropertyAlsoToAllAttributedDataPoints( const rtl::Reference< ::chart::DataSeries >& xSeries,
453 const OUString& rPropertyName, const uno::Any& rPropertyValue )
455 if( !xSeries.is() )
456 return;
458 xSeries->setPropertyValue( rPropertyName, rPropertyValue );
459 uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
460 // "AttributedDataPoints"
461 if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList )
463 for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
465 Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
466 if(!xPointProp.is())
467 continue;
468 xPointProp->setPropertyValue( rPropertyName, rPropertyValue );
469 if( rPropertyName == "LabelPlacement" )
470 xPointProp->setPropertyValue("CustomLabelPosition", uno::Any());
475 bool hasAttributedDataPointDifferentValue( const rtl::Reference< DataSeries >& xSeries,
476 const OUString& rPropertyName, const uno::Any& rPropertyValue )
478 if( !xSeries.is() )
479 return false;
481 uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
482 // "AttributedDataPoints"
483 if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList )
485 for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
487 Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
488 if(!xPointProp.is())
489 continue;
490 uno::Any aPointValue( xPointProp->getPropertyValue( rPropertyName ) );
491 if( rPropertyValue != aPointValue )
492 return true;
495 return false;
498 namespace
503 sal_Int32 translateIndexFromHiddenToFullSequence( sal_Int32 nIndex, const Reference< chart2::data::XDataSequence >& xDataSequence, bool bTranslate )
505 if( !bTranslate )
506 return nIndex;
510 uno::Reference<beans::XPropertySet> xProp( xDataSequence, uno::UNO_QUERY );
511 if( xProp.is())
513 Sequence<sal_Int32> aHiddenIndicesSeq;
514 xProp->getPropertyValue( "HiddenValues" ) >>= aHiddenIndicesSeq;
515 if( aHiddenIndicesSeq.hasElements() )
517 auto aHiddenIndices( comphelper::sequenceToContainer<std::vector< sal_Int32 >>( aHiddenIndicesSeq ) );
518 std::sort( aHiddenIndices.begin(), aHiddenIndices.end() );
520 sal_Int32 nHiddenCount = static_cast<sal_Int32>(aHiddenIndices.size());
521 for( sal_Int32 nN = 0; nN < nHiddenCount; ++nN)
523 if( aHiddenIndices[nN] <= nIndex )
524 nIndex += 1;
525 else
526 break;
531 catch (const beans::UnknownPropertyException&)
534 return nIndex;
537 bool hasDataLabelsAtSeries( const rtl::Reference< DataSeries >& xSeries )
539 bool bRet = false;
542 if( xSeries.is() )
544 DataPointLabel aLabel;
545 if( xSeries->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
546 bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent || aLabel.ShowCategoryName
547 || aLabel.ShowSeriesName;
550 catch(const uno::Exception &)
552 TOOLS_WARN_EXCEPTION("chart2", "" );
554 return bRet;
557 bool hasDataLabelsAtPoints( const rtl::Reference< DataSeries >& xSeries )
559 bool bRet = false;
562 if( xSeries.is() )
564 uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
565 // "AttributedDataPoints"
566 if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList )
568 for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
570 Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
571 if( xPointProp.is() )
573 DataPointLabel aLabel;
574 if( xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
575 bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent
576 || aLabel.ShowCategoryName || aLabel.ShowCustomLabel
577 || aLabel.ShowSeriesName;
578 if( bRet )
579 break;
585 catch(const uno::Exception &)
587 TOOLS_WARN_EXCEPTION("chart2", "" );
589 return bRet;
592 bool hasDataLabelAtPoint( const rtl::Reference< DataSeries >& xSeries, sal_Int32 nPointIndex )
594 bool bRet = false;
597 Reference< beans::XPropertySet > xProp;
598 if( xSeries.is() )
600 uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
601 // "AttributedDataPoints"
602 if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList )
604 auto aIt = std::find( std::as_const(aAttributedDataPointIndexList).begin(), std::as_const(aAttributedDataPointIndexList).end(), nPointIndex );
605 if( aIt != std::as_const(aAttributedDataPointIndexList).end())
606 xProp = xSeries->getDataPointByIndex(nPointIndex);
607 else
608 xProp = xSeries;
610 if( xProp.is() )
612 DataPointLabel aLabel;
613 if( xProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
614 bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent
615 || aLabel.ShowCategoryName || aLabel.ShowCustomLabel
616 || aLabel.ShowSeriesName;
620 catch(const uno::Exception &)
622 TOOLS_WARN_EXCEPTION("chart2", "" );
624 return bRet;
627 void insertDataLabelsToSeriesAndAllPoints( const rtl::Reference< DataSeries >& xSeries )
629 lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( xSeries, true /*bInsert*/ );
632 void deleteDataLabelsFromSeriesAndAllPoints( const rtl::Reference< DataSeries >& xSeries )
634 lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( xSeries, false /*bInsert*/ );
637 void insertDataLabelToPoint( const Reference< beans::XPropertySet >& xPointProp )
641 if( xPointProp.is() )
643 DataPointLabel aLabel;
644 xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel;
645 aLabel.ShowNumber = true;
646 xPointProp->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel));
649 catch(const uno::Exception &)
651 TOOLS_WARN_EXCEPTION("chart2", "" );
655 void deleteDataLabelsFromPoint( const Reference< beans::XPropertySet >& xPointProp )
659 if( xPointProp.is() )
661 DataPointLabel aLabel;
662 xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel;
663 aLabel.ShowNumber = false;
664 aLabel.ShowNumberInPercent = false;
665 aLabel.ShowCategoryName = false;
666 aLabel.ShowCustomLabel = false;
667 aLabel.ShowSeriesName = false;
668 xPointProp->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel));
669 xPointProp->setPropertyValue(CHART_UNONAME_CUSTOM_LABEL_FIELDS, uno::Any());
672 catch(const uno::Exception &)
674 TOOLS_WARN_EXCEPTION("chart2", "" );
678 } // namespace chart
680 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */