1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "SchXMLChartContext.hxx"
21 #include <SchXMLImport.hxx>
22 #include "SchXMLLegendContext.hxx"
23 #include "SchXMLDataTableContext.hxx"
24 #include "SchXMLPlotAreaContext.hxx"
25 #include "SchXMLParagraphContext.hxx"
26 #include "SchXMLTableContext.hxx"
27 #include "SchXMLSeries2Context.hxx"
28 #include "SchXMLTools.hxx"
29 #include <osl/diagnose.h>
30 #include <sal/log.hxx>
31 #include <comphelper/diagnose_ex.hxx>
32 #include <unotools/mediadescriptor.hxx>
33 #include <utility>
34 #include <xmloff/xmlnamespace.hxx>
35 #include <xmloff/xmltoken.hxx>
36 #include <xmloff/namespacemap.hxx>
37 #include <xmloff/xmluconv.hxx>
38 #include <xmloff/xmlstyle.hxx>
39 #include <xmloff/SchXMLSeriesHelper.hxx>
40 
41 #include <vector>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/chart/XChartDocument.hpp>
44 #include <com/sun/star/chart/XDiagram.hpp>
45 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
46 #include <com/sun/star/embed/Aspects.hpp>
47 #include <com/sun/star/embed/XVisualObject.hpp>
48 
49 #include <com/sun/star/chart2/XChartDocument.hpp>
50 #include <com/sun/star/chart2/data/XDataSink.hpp>
51 #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
52 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
53 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
54 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
55 #include <com/sun/star/chart2/XTitled.hpp>
56 
57 #include <com/sun/star/container/XChild.hpp>
58 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
59 #include <o3tl/safeint.hxx>
60 #include <o3tl/string_view.hxx>
61 
62 using namespace com::sun::star;
63 using namespace ::xmloff::token;
64 using com::sun::star::uno::Reference;
65 using namespace ::SchXMLTools;
66 
67 namespace
68 {
69 
70 void lcl_setRoleAtLabeledSequence(
71     const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq,
72     const OUString &rRole )
73 {
74     // set role of sequence
75     uno::Reference< chart2::data::XDataSequence > xValues( xLSeq->getValues());
76     if( xValues.is())
77     {
78         uno::Reference< beans::XPropertySet > xProp( xValues, uno::UNO_QUERY );
79         if( xProp.is())
80             xProp->setPropertyValue("Role", uno::Any( rRole ));
81     }
82 }
83 
84 void lcl_MoveDataToCandleStickSeries(
85     const uno::Reference< chart2::data::XDataSource > & xDataSource,
86     const uno::Reference< chart2::XDataSeries > & xDestination,
87     const OUString & rRole )
88 {
89     try
90     {
91         uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledSeq(
92             xDataSource->getDataSequences());
93         if( aLabeledSeq.hasElements())
94         {
95             lcl_setRoleAtLabeledSequence( aLabeledSeq[0], rRole );
96 
97             // add to data series
98             uno::Reference< chart2::data::XDataSource > xSource( xDestination, uno::UNO_QUERY_THROW );
99             // @todo: realloc only once outside this function
100             uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aData( xSource->getDataSequences());
101             aData.realloc( aData.getLength() + 1);
102             aData.getArray()[ aData.getLength() - 1 ] = aLabeledSeq[0];
103             uno::Reference< chart2::data::XDataSink > xSink( xDestination, uno::UNO_QUERY_THROW );
104             xSink->setData( aData );
105         }
106     }
107     catch(const uno::Exception&)
108     {
109         TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception caught while moving data to candlestick series" );
110     }
111 }
112 
113 void lcl_setRoleAtFirstSequence(
114     const uno::Reference< chart2::XDataSeries > & xSeries,
115     const OUString & rRole )
116 {
117     uno::Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
118     if( xSource.is())
119     {
120         uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSeq( xSource->getDataSequences());
121         if( aSeq.hasElements())
122             lcl_setRoleAtLabeledSequence( aSeq[0], rRole );
123     }
124 }
125 
126 void lcl_removeEmptyChartTypeGroups( const uno::Reference< chart2::XChartDocument > & xDoc )
127 {
128     if( ! xDoc.is())
129         return;
130 
131     uno::Reference< chart2::XDiagram > xDia( xDoc->getFirstDiagram());
132     if( ! xDia.is())
133         return;
134 
135     try
136     {
137         // count all charttype groups to be able to leave at least one
138         sal_Int32 nRemainingGroups = 0;
139         uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDia, uno::UNO_QUERY_THROW );
140         const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > >
141             aCooSysSeq( xCooSysCnt->getCoordinateSystems());
142         for( auto const & i : aCooSysSeq )
143         {
144             uno::Reference< chart2::XChartTypeContainer > xCTCnt( i, uno::UNO_QUERY_THROW );
145             nRemainingGroups += xCTCnt->getChartTypes().getLength();
146         }
147 
148         // delete all empty groups, but leave at least  group (empty or not)
149         for( sal_Int32 nI = aCooSysSeq.getLength(); nI-- && (nRemainingGroups > 1); )
150         {
151             uno::Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nI], uno::UNO_QUERY_THROW );
152             uno::Sequence< uno::Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
153             for( sal_Int32 nJ=aCTSeq.getLength(); nJ-- && (nRemainingGroups > 1); )
154             {
155                 uno::Reference< chart2::XDataSeriesContainer > xDSCnt( aCTSeq[nJ], uno::UNO_QUERY_THROW );
156                 if( !xDSCnt->getDataSeries().hasElements() )
157                 {
158                     // note: iterator stays valid as we have a local sequence
159                     xCTCnt->removeChartType( aCTSeq[nJ] );
160                     --nRemainingGroups;
161                 }
162             }
163         }
164     }
165     catch(const uno::Exception&)
166     {
167         TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught while removing empty chart types");
168     }
169 }
170 
171 uno::Sequence< sal_Int32 > lcl_getNumberSequenceFromString( std::u16string_view rStr, bool bAddOneToEachOldIndex )
172 {
173     const sal_Unicode aSpace( ' ' );
174 
175     // count number of entries
176     ::std::vector< sal_Int32 > aVec;
177     size_t nLastPos = 0;
178     size_t nPos = 0;
179     while( nPos != std::u16string_view::npos )
180     {
181         nPos = rStr.find( aSpace, nLastPos );
182         if( nPos != std::u16string_view::npos )
183         {
184             if( nPos > nLastPos )
185                 aVec.push_back( o3tl::toInt32(rStr.substr( nLastPos, (nPos - nLastPos) )) );
186             nLastPos = nPos + 1;
187         }
188     }
189     // last entry
190     if( nLastPos != 0 &&
191         rStr.size() > nLastPos )
192     {
193         aVec.push_back( o3tl::toInt32(rStr.substr( nLastPos )) );
194     }
195 
196     const size_t nVecSize = aVec.size();
197     uno::Sequence< sal_Int32 > aSeq( nVecSize );
198 
199     if(!bAddOneToEachOldIndex)
200     {
201         sal_Int32* pSeqArr = aSeq.getArray();
202         for( nPos = 0; nPos < nVecSize; ++nPos )
203         {
204             pSeqArr[ nPos ] = aVec[ nPos ];
205         }
206     }
207     else if( bAddOneToEachOldIndex )
208     {
209         aSeq.realloc( nVecSize+1 );
210         auto pSeqArr = aSeq.getArray();
211         pSeqArr[0]=0;
212 
213         for( nPos = 0; nPos < nVecSize; ++nPos )
214         {
215             pSeqArr[ nPos+1 ] = aVec[ nPos ]+1;
216         }
217     }
218 
219     return aSeq;
220 }
221 
222 } // anonymous namespace
223 
224 SchXMLChartContext::SchXMLChartContext( SchXMLImportHelper& rImpHelper,
225                                         SvXMLImport& rImport ) :
226         SvXMLImportContext( rImport ),
227         mrImportHelper( rImpHelper ),
228         m_bHasRangeAtPlotArea( false ),
229         m_bHasTableElement( false ),
230         mbAllRangeAddressesAvailable( true ),
231         mbColHasLabels( false ),
232         mbRowHasLabels( false ),
233         meDataRowSource( chart::ChartDataRowSource_COLUMNS ),
234         mbIsStockChart( false ),
235         mPieSubType(com::sun::star::chart2::PieChartSubType_NONE)
236 {
237 }
238 
239 SchXMLChartContext::~SchXMLChartContext()
240 {}
241 
242 static bool lcl_hasServiceName(Reference<lang::XMultiServiceFactory> const & xFactory, OUString const & rServiceName)
243 {
244     const uno::Sequence<OUString> aServiceNames(xFactory->getAvailableServiceNames());
245 
246     return std::find(aServiceNames.begin(), aServiceNames.end(), rServiceName) != aServiceNames.end();
247 }
248 
249 void setDataProvider(uno::Reference<chart2::XChartDocument> const & xChartDoc, OUString const & sDataPilotSource)
250 {
251     if (!xChartDoc.is())
252         return;
253 
254     try
255     {
256         uno::Reference<container::XChild> xChild(xChartDoc, uno::UNO_QUERY);
257         uno::Reference<chart2::data::XDataReceiver> xDataReceiver(xChartDoc, uno::UNO_QUERY);
258         if (xChild.is() && xDataReceiver.is())
259         {
260             bool bHasOwnData = true;
261 
262             Reference<lang::XMultiServiceFactory> xFact(xChild->getParent(), uno::UNO_QUERY);
263             if (xFact.is())
264             {
265                 if (!xChartDoc->getDataProvider().is())
266                 {
267                     bool bHasDataPilotSource = !sDataPilotSource.isEmpty();
268                     OUString aDataProviderServiceName("com.sun.star.chart2.data.DataProvider");
269                     if (bHasDataPilotSource)
270                         aDataProviderServiceName = "com.sun.star.chart2.data.PivotTableDataProvider";
271 
272                     if (lcl_hasServiceName(xFact, aDataProviderServiceName))
273                     {
274                         Reference<chart2::data::XDataProvider> xProvider(xFact->createInstance(aDataProviderServiceName), uno::UNO_QUERY);
275 
276                         if (xProvider.is())
277                         {
278                             if (bHasDataPilotSource)
279                             {
280                                 Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xProvider, uno::UNO_QUERY);
281                                 xPivotTableDataProvider->setPivotTableName(sDataPilotSource);
282                                 xDataReceiver->attachDataProvider(xProvider);
283                                 bHasOwnData = !xPivotTableDataProvider->hasPivotTable();
284                             }
285                             else
286                             {
287                                 xDataReceiver->attachDataProvider(xProvider);
288                                 bHasOwnData = false;
289                             }
290                         }
291                     }
292                 }
293                 else
294                     bHasOwnData = false;
295             }
296             // else we have no parent => we have our own data
297 
298             if (bHasOwnData && ! xChartDoc->hasInternalDataProvider())
299                 xChartDoc->createInternalDataProvider(false);
300         }
301     }
302     catch (const uno::Exception &)
303     {
304         TOOLS_INFO_EXCEPTION("xmloff.chart", "SchXMLChartContext::StartElement()");
305     }
306 }
307 
308 void SchXMLChartContext::startFastElement( sal_Int32 /*nElement*/,
309     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
310 {
311     // parse attributes
312 
313     uno::Reference< embed::XVisualObject > xVisualObject( mrImportHelper.GetChartDocument(), uno::UNO_QUERY);
314     SAL_WARN_IF(!xVisualObject.is(), "xmloff.chart", "need xVisualObject for page size");
315     if( xVisualObject.is() )
316         maChartSize = xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); //#i103460# take the size given from the parent frame as default
317 
318     OUString sAutoStyleName;
319     OUString aOldChartTypeName;
320     bool bHasAddin = false;
321     mPieSubType = com::sun::star::chart2::PieChartSubType_NONE;
322 
323     for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
324     {
325         switch( aIter.getToken() )
326         {
327             case XML_ELEMENT(LO_EXT, XML_DATA_PILOT_SOURCE):
328                 msDataPilotSource = aIter.toString();
329                 break;
330             case XML_ELEMENT(XLINK, XML_HREF):
331                 m_aXLinkHRefAttributeToIndicateDataProvider = aIter.toString();
332                 break;
333             case XML_ELEMENT(CHART, XML_CLASS):
334                 {
335                     OUString aValue = aIter.toString();
336                     OUString sClassName;
337                     sal_uInt16 nClassPrefix =
338                         GetImport().GetNamespaceMap().GetKeyByAttrValueQName(
339                                 aValue, &sClassName );
340                     if( XML_NAMESPACE_CHART == nClassPrefix )
341                     {
342                         SchXMLChartTypeEnum eChartTypeEnum = SchXMLTools::GetChartTypeEnum( sClassName );
343                         if( eChartTypeEnum != XML_CHART_CLASS_UNKNOWN )
344                         {
345                             aOldChartTypeName = SchXMLTools::GetChartTypeByClassName( sClassName, true /* bUseOldNames */ );
346                             maChartTypeServiceName = SchXMLTools::GetChartTypeByClassName( sClassName, false /* bUseOldNames */ );
347                             switch( eChartTypeEnum )
348                             {
349                             case XML_CHART_CLASS_STOCK:
350                                 mbIsStockChart = true;
351                                 break;
352                             default:
353                                 break;
354                             }
355                         }
356                     }
357                     else if( XML_NAMESPACE_OOO == nClassPrefix )
358                     {
359                         // service is taken from add-in-name attribute
360                         bHasAddin = true;
361 
362                         aOldChartTypeName = sClassName;
363                         maChartTypeServiceName = sClassName;
364                     }
365                 }
366                 break;
367 
368             case XML_ELEMENT(SVG, XML_WIDTH):
369             case XML_ELEMENT(SVG_COMPAT, XML_WIDTH):
370                 GetImport().GetMM100UnitConverter().convertMeasureToCore(
371                         maChartSize.Width, aIter.toView() );
372                 break;
373 
374             case XML_ELEMENT(SVG, XML_HEIGHT):
375             case XML_ELEMENT(SVG_COMPAT, XML_HEIGHT):
376                 GetImport().GetMM100UnitConverter().convertMeasureToCore(
377                         maChartSize.Height, aIter.toView() );
378                 break;
379 
380             case XML_ELEMENT(CHART, XML_STYLE_NAME):
381                 sAutoStyleName = aIter.toString();
382                 break;
383 
384             case XML_ELEMENT(CHART, XML_COLUMN_MAPPING):
385                 msColTrans = aIter.toString();
386                 break;
387             case XML_ELEMENT(CHART,  XML_ROW_MAPPING):
388                 msRowTrans = aIter.toString();
389                 break;
390             case XML_ELEMENT(LO_EXT, XML_SUB_BAR):
391                 if (aIter.toString().toBoolean()) {
392                     mPieSubType = com::sun::star::chart2::PieChartSubType_BAR;
393                 }
394                 break;
395             case XML_ELEMENT(LO_EXT, XML_SUB_PIE):
396                 if (aIter.toString().toBoolean()) {
397                     mPieSubType = com::sun::star::chart2::PieChartSubType_PIE;
398                 }
399                 break;
400             default:
401                 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
402         }
403     }
404 
405     uno::Reference<chart::XChartDocument> xDoc = mrImportHelper.GetChartDocument();
406     uno::Reference<chart2::XChartDocument> xNewDoc(xDoc, uno::UNO_QUERY);
407 
408     setDataProvider(xNewDoc, msDataPilotSource);
409 
410     if( aOldChartTypeName.isEmpty() )
411     {
412         SAL_WARN("xmloff.chart", "need a charttype to create a diagram" );
413         //set a fallback value:
414         const OUString& aChartClass_Bar( GetXMLToken(XML_BAR ) );
415         aOldChartTypeName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, true /* bUseOldNames */ );
416         maChartTypeServiceName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, false /* bUseOldNames */ );
417     }
418 
419     //  Set the size of the draw page.
420     if( xVisualObject.is() )
421         xVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, maChartSize );
422 
423     InitChart( aOldChartTypeName);
424 
425     if( bHasAddin )
426     {
427         //correct charttype service name when having an addin
428         //and don't refresh addin during load
429         uno::Reference< beans::XPropertySet > xDocProp( mrImportHelper.GetChartDocument(), uno::UNO_QUERY );
430         if( xDocProp.is() )
431         {
432             try
433             {
434                 xDocProp->getPropertyValue("BaseDiagram") >>= aOldChartTypeName;
435                 maChartTypeServiceName =  SchXMLTools::GetNewChartTypeName( aOldChartTypeName );
436                 xDocProp->setPropertyValue("RefreshAddInAllowed", uno::Any( false) );
437             }
438             catch(const uno::Exception&)
439             {
440                 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::StartElement" );
441             }
442         }
443     }
444 
445     // set auto-styles for Area
446     uno::Reference<beans::XPropertySet> xProp = mrImportHelper.GetChartDocument()->getArea();
447     mrImportHelper.FillAutoStyle(sAutoStyleName, xProp);
448 }
449 
450 namespace
451 {
452 
453 struct NewDonutSeries
454 {
455     css::uno::Reference< css::chart2::XDataSeries > m_xSeries;
456     OUString msStyleName;
457     sal_Int32 mnAttachedAxis;
458 
459     ::std::vector< OUString > m_aSeriesStyles;
460     ::std::vector< OUString > m_aPointStyles;
461 
462     NewDonutSeries( css::uno::Reference< css::chart2::XDataSeries > xSeries, sal_Int32 nPointCount )
463                     : m_xSeries(std::move( xSeries ))
464                     , mnAttachedAxis( 1 )
465     {
466         m_aPointStyles.resize(nPointCount);
467         m_aSeriesStyles.resize(nPointCount);
468     }
469 
470     void setSeriesStyleNameToPoint( const OUString& rStyleName, sal_Int32 nPointIndex )
471     {
472         SAL_WARN_IF(nPointIndex >= static_cast<sal_Int32>(m_aSeriesStyles.size()), "xmloff.chart", "donut point <-> series count mismatch");
473         if( nPointIndex < static_cast<sal_Int32>(m_aSeriesStyles.size()) )
474             m_aSeriesStyles[nPointIndex]=rStyleName;
475     }
476 
477     void setPointStyleNameToPoint( const OUString& rStyleName, sal_Int32 nPointIndex )
478     {
479         SAL_WARN_IF(nPointIndex >= static_cast<sal_Int32>(m_aPointStyles.size()), "xmloff.chart", "donut point <-> series count mismatch");
480         if( nPointIndex < static_cast<sal_Int32>(m_aPointStyles.size()) )
481             m_aPointStyles[nPointIndex]=rStyleName;
482     }
483 
484     ::std::vector< DataRowPointStyle > creatStyleVector()
485     {
486         ::std::vector< DataRowPointStyle > aRet;
487 
488         DataRowPointStyle aSeriesStyle( DataRowPointStyle::DATA_SERIES
489             , m_xSeries, -1, 1, msStyleName, mnAttachedAxis );
490         aRet.push_back( aSeriesStyle );
491 
492         sal_Int32 nPointIndex=0;
493         for (auto const& pointStyle : m_aPointStyles)
494         {
495             DataRowPointStyle aPointStyle( DataRowPointStyle::DATA_POINT
496                 , m_xSeries, nPointIndex, 1, pointStyle, mnAttachedAxis );
497             if( nPointIndex < static_cast<sal_Int32>(m_aSeriesStyles.size()) )
498             {
499                 aPointStyle.msSeriesStyleNameForDonuts = m_aSeriesStyles[nPointIndex];
500             }
501             if( !aPointStyle.msSeriesStyleNameForDonuts.isEmpty()
502                 || !aPointStyle.msStyleName.isEmpty() )
503                 aRet.push_back( aPointStyle );
504             ++nPointIndex;
505         }
506 
507         return aRet;
508     }
509 };
510 
511 void lcl_swapPointAndSeriesStylesForDonutCharts( ::std::vector< DataRowPointStyle >& rStyleVector
512         , ::std::map< css::uno::Reference< css::chart2::XDataSeries> , sal_Int32 >&& aSeriesMap )
513 {
514     //detect old series count
515     //and add old series to aSeriesMap
516     sal_Int32 nOldSeriesCount = 0;
517     {
518         sal_Int32 nMaxOldSeriesIndex = 0;
519         sal_Int32 nOldSeriesIndex = 0;
520         for (auto const& style : rStyleVector)
521         {
522             DataRowPointStyle aStyle(style);
523             if(aStyle.meType == DataRowPointStyle::DATA_SERIES &&
524                     aStyle.m_xSeries.is() )
525             {
526                 nMaxOldSeriesIndex = nOldSeriesIndex;
527 
528                 if( aSeriesMap.end() == aSeriesMap.find(aStyle.m_xSeries) )
529                     aSeriesMap[aStyle.m_xSeries] = nOldSeriesIndex;
530 
531                 nOldSeriesIndex++;
532             }
533         }
534         nOldSeriesCount = nMaxOldSeriesIndex+1;
535     }
536 
537     //initialize new series styles
538     ::std::map< Reference< chart2::XDataSeries >, sal_Int32 >::const_iterator aSeriesMapEnd( aSeriesMap.end() );
539 
540     //sort by index
541     ::std::vector< NewDonutSeries > aNewSeriesVector;
542     {
543         ::std::map< sal_Int32, Reference< chart2::XDataSeries > > aIndexSeriesMap;
544         for (auto const& series : aSeriesMap)
545             aIndexSeriesMap[series.second] = series.first;
546 
547         for (auto const& indexSeries : aIndexSeriesMap)
548             aNewSeriesVector.emplace_back(indexSeries.second,nOldSeriesCount );
549     }
550 
551     //overwrite attached axis information according to old series styles
552     for (auto const& style : rStyleVector)
553     {
554         DataRowPointStyle aStyle(style);
555         if(aStyle.meType == DataRowPointStyle::DATA_SERIES )
556         {
557             auto aSeriesMapIt = aSeriesMap.find( aStyle.m_xSeries );
558             if( aSeriesMapIt != aSeriesMapEnd && aSeriesMapIt->second < static_cast<sal_Int32>(aNewSeriesVector.size()) )
559                 aNewSeriesVector[aSeriesMapIt->second].mnAttachedAxis = aStyle.mnAttachedAxis;
560         }
561     }
562 
563     //overwrite new series style names with old series style name information
564     for (auto const& style : rStyleVector)
565     {
566         DataRowPointStyle aStyle(style);
567         if( aStyle.meType == DataRowPointStyle::DATA_SERIES )
568         {
569             auto aSeriesMapIt = aSeriesMap.find(aStyle.m_xSeries);
570             if( aSeriesMapEnd != aSeriesMapIt )
571             {
572                 sal_Int32 nNewPointIndex = aSeriesMapIt->second;
573 
574                 for (auto & newSeries : aNewSeriesVector)
575                     newSeries.setSeriesStyleNameToPoint( aStyle.msStyleName, nNewPointIndex );
576             }
577         }
578     }
579 
580     //overwrite new series style names with point style name information
581     for (auto const& style : rStyleVector)
582     {
583         DataRowPointStyle aStyle(style);
584         if( aStyle.meType == DataRowPointStyle::DATA_POINT )
585         {
586             auto aSeriesMapIt = aSeriesMap.find(aStyle.m_xSeries);
587             if( aSeriesMapEnd != aSeriesMapIt )
588             {
589                 sal_Int32 nNewPointIndex = aSeriesMapIt->second;
590                 sal_Int32 nNewSeriesIndex = aStyle.m_nPointIndex;
591                 sal_Int32 nRepeatCount = aStyle.m_nPointRepeat;
592 
593                 while( nRepeatCount && (nNewSeriesIndex>=0) && (o3tl::make_unsigned(nNewSeriesIndex)< aNewSeriesVector.size() ) )
594                 {
595                     NewDonutSeries& rNewSeries( aNewSeriesVector[nNewSeriesIndex] );
596                     rNewSeries.setPointStyleNameToPoint( aStyle.msStyleName, nNewPointIndex );
597 
598                     nRepeatCount--;
599                     nNewSeriesIndex++;
600                 }
601             }
602         }
603     }
604 
605     //put information from aNewSeriesVector to output parameter rStyleVector
606     rStyleVector.clear();
607 
608     for (auto & newSeries : aNewSeriesVector)
609     {
610         ::std::vector< DataRowPointStyle > aVector( newSeries.creatStyleVector() );
611         rStyleVector.insert(rStyleVector.end(),aVector.begin(),aVector.end());
612     }
613 }
614 
615 bool lcl_SpecialHandlingForDonutChartNeeded(
616     std::u16string_view rServiceName,
617     const SvXMLImport & rImport )
618 {
619     bool bResult = false;
620     if( rServiceName == u"com.sun.star.chart2.DonutChartType" )
621     {
622         bResult = SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( rImport.GetModel() );
623     }
624     return bResult;
625 }
626 
627 } // anonymous namespace
628 
629 static void lcl_ApplyDataFromRectangularRangeToDiagram(
630         const uno::Reference< chart2::XChartDocument >& xNewDoc
631         , const OUString& rRectangularRange
632         , css::chart::ChartDataRowSource eDataRowSource
633         , bool bRowHasLabels, bool bColHasLabels
634         , bool bSwitchOnLabelsAndCategoriesForOwnData
635         , std::u16string_view sColTrans
636         , std::u16string_view sRowTrans )
637 {
638     if( !xNewDoc.is() )
639         return;
640 
641     uno::Reference< chart2::XDiagram > xNewDia( xNewDoc->getFirstDiagram());
642     uno::Reference< chart2::data::XDataProvider > xDataProvider( xNewDoc->getDataProvider() );
643     if( !xNewDia.is() || !xDataProvider.is() )
644         return;
645 
646     bool bFirstCellAsLabel =
647         (eDataRowSource==chart::ChartDataRowSource_COLUMNS)? bRowHasLabels : bColHasLabels;
648     bool bHasCateories =
649         (eDataRowSource==chart::ChartDataRowSource_COLUMNS)? bColHasLabels : bRowHasLabels;
650 
651     if( bSwitchOnLabelsAndCategoriesForOwnData )
652     {
653         bFirstCellAsLabel = true;
654         bHasCateories = true;
655     }
656 
657     uno::Sequence< beans::PropertyValue > aArgs{
658         beans::PropertyValue(
659            "CellRangeRepresentation",
660            -1, uno::Any( rRectangularRange ),
661            beans::PropertyState_DIRECT_VALUE ),
662         beans::PropertyValue(
663            "DataRowSource",
664            -1, uno::Any( eDataRowSource ),
665            beans::PropertyState_DIRECT_VALUE ),
666         beans::PropertyValue(
667            "FirstCellAsLabel",
668            -1, uno::Any( bFirstCellAsLabel ),
669            beans::PropertyState_DIRECT_VALUE )
670     };
671 
672     if( !sColTrans.empty() || !sRowTrans.empty() )
673     {
674         aArgs.realloc( aArgs.getLength() + 1 );
675         aArgs.getArray()[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue(
676             "SequenceMapping",
677             -1, uno::Any( !sColTrans.empty()
678                 ? lcl_getNumberSequenceFromString( sColTrans, bHasCateories && !xNewDoc->hasInternalDataProvider() )
679                 : lcl_getNumberSequenceFromString( sRowTrans, bHasCateories && !xNewDoc->hasInternalDataProvider() ) ),
680         beans::PropertyState_DIRECT_VALUE );
681     }
682 
683     //work around wrong writer ranges ( see Issue 58464 )
684     {
685         OUString aChartOleObjectName;
686         if( xNewDoc.is() )
687         {
688             utl::MediaDescriptor aMediaDescriptor( xNewDoc->getArgs() );
689 
690             utl::MediaDescriptor::const_iterator aIt(
691                 aMediaDescriptor.find( OUString(  "HierarchicalDocumentName" )));
692             if( aIt != aMediaDescriptor.end() )
693             {
694                 aChartOleObjectName = (*aIt).second.get< OUString >();
695             }
696         }
697         if( !aChartOleObjectName.isEmpty() )
698         {
699             aArgs.realloc( aArgs.getLength() + 1 );
700             aArgs.getArray()[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue(
701                 "ChartOleObjectName",
702                 -1, uno::Any( aChartOleObjectName ),
703                 beans::PropertyState_DIRECT_VALUE );
704         }
705     }
706 
707     uno::Reference< chart2::data::XDataSource > xDataSource(
708         xDataProvider->createDataSource( aArgs ));
709 
710     aArgs.realloc( aArgs.getLength() + 2 );
711     auto pArgs = aArgs.getArray();
712     pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 2 ] = beans::PropertyValue(
713         "HasCategories",
714         -1, uno::Any( bHasCateories ),
715         beans::PropertyState_DIRECT_VALUE );
716     pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue(
717         "UseCategoriesAsX",
718         -1, uno::Any( false ),//categories in ODF files are not to be used as x values (independent from what is offered in our ui)
719         beans::PropertyState_DIRECT_VALUE );
720 
721     xNewDia->setDiagramData( xDataSource, aArgs );
722 }
723 
724 void SchXMLChartContext::endFastElement(sal_Int32 )
725 {
726     uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument();
727     uno::Reference< beans::XPropertySet > xProp( xDoc, uno::UNO_QUERY );
728     uno::Reference< chart2::XChartDocument > xNewDoc( xDoc, uno::UNO_QUERY );
729 
730     if( xProp.is())
731     {
732         if( !maMainTitle.isEmpty())
733         {
734             uno::Reference< beans::XPropertySet > xTitleProp( xDoc->getTitle(), uno::UNO_QUERY );
735             if( xTitleProp.is())
736             {
737                 try
738                 {
739                     // TODO: ODF import for formatted chart titles
740                     xTitleProp->setPropertyValue("String", uno::Any(maMainTitle) );
741                 }
742                 catch(const beans::UnknownPropertyException&)
743                 {
744                     SAL_WARN("xmloff.chart", "Property String for Title not available" );
745                 }
746             }
747         }
748         if( !maSubTitle.isEmpty())
749         {
750             uno::Reference< beans::XPropertySet > xTitleProp( xDoc->getSubTitle(), uno::UNO_QUERY );
751             if( xTitleProp.is())
752             {
753                 try
754                 {
755                     // TODO: ODF import for formatted chart titles
756                     xTitleProp->setPropertyValue("String", uno::Any(maSubTitle) );
757                 }
758                 catch(const beans::UnknownPropertyException&)
759                 {
760                     SAL_WARN("xmloff.chart", "Property String for Title not available" );
761                 }
762             }
763         }
764     }
765 
766     // cleanup: remove empty chart type groups
767     lcl_removeEmptyChartTypeGroups( xNewDoc );
768 
769     // Handle sub-pie type. Is this the right place to do this?
770     if (maChartTypeServiceName == "com.sun.star.chart2.PieChartType") {
771         Reference< chart2::XDiagram> xDia(xNewDoc->getFirstDiagram());
772         uno::Reference< beans::XPropertySet > xDiaProp( xDia, uno::UNO_QUERY );
773         if( xDiaProp.is()) {
774             xDiaProp->setPropertyValue("SubPieType", uno::Any(mPieSubType));
775         }
776     }
777 
778     // set stack mode before a potential chart type detection (in case we have a rectangular range)
779     uno::Reference< chart::XDiagram > xDiagram( xDoc->getDiagram() );
780     uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY );
781     if( xDiaProp.is())
782     {
783         if( maSeriesDefaultsAndStyles.maStackedDefault.hasValue())
784             xDiaProp->setPropertyValue("Stacked",maSeriesDefaultsAndStyles.maStackedDefault);
785         if( maSeriesDefaultsAndStyles.maPercentDefault.hasValue())
786             xDiaProp->setPropertyValue("Percent",maSeriesDefaultsAndStyles.maPercentDefault);
787         if( maSeriesDefaultsAndStyles.maDeepDefault.hasValue())
788             xDiaProp->setPropertyValue("Deep",maSeriesDefaultsAndStyles.maDeepDefault);
789         if( maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault.hasValue())
790             xDiaProp->setPropertyValue("StackedBarsConnected",maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault);
791     }
792 
793     //the OOo 2.0 implementation and older has a bug with donuts
794     bool bSpecialHandlingForDonutChart = lcl_SpecialHandlingForDonutChartNeeded(
795         maChartTypeServiceName, GetImport());
796 
797     // apply data
798     if(!xNewDoc.is())
799         return;
800 
801     bool bHasOwnData = false;
802     if( m_aXLinkHRefAttributeToIndicateDataProvider == "." ) //data comes from the chart itself
803         bHasOwnData = true;
804     else if( m_aXLinkHRefAttributeToIndicateDataProvider == ".." ) //data comes from the parent application
805         bHasOwnData = false;
806     else if( !m_aXLinkHRefAttributeToIndicateDataProvider.isEmpty() ) //not supported so far to get the data by sibling objects -> fall back to chart itself if data are available
807         bHasOwnData = m_bHasTableElement;
808     else
809         bHasOwnData = !m_bHasRangeAtPlotArea;
810 
811     if( xNewDoc->hasInternalDataProvider())
812     {
813         if( !m_bHasTableElement && m_aXLinkHRefAttributeToIndicateDataProvider != "." )
814         {
815             //#i103147# ODF, workaround broken files with a missing table:cell-range-address at the plot-area
816             bool bSwitchSuccessful = SchXMLTools::switchBackToDataProviderFromParent( xNewDoc, maLSequencesPerIndex );
817             bHasOwnData = !bSwitchSuccessful;
818         }
819         else
820             bHasOwnData = true;//e.g. in case of copy->paste from calc to impress
821     }
822     else if( bHasOwnData )
823     {
824         xNewDoc->createInternalDataProvider( false /* bCloneExistingData */ );
825     }
826     if( bHasOwnData )
827         msChartAddress = "all";
828 
829     bool bSwitchRangesFromOuterToInternalIfNecessary = false;
830     if( !bHasOwnData && mbAllRangeAddressesAvailable )
831     {
832         // special handling for stock chart (merge series together)
833         if( mbIsStockChart )
834             MergeSeriesForStockChart();
835     }
836     else if( !msChartAddress.isEmpty() )
837     {
838         //own data or only rectangular range available
839 
840         if( xNewDoc->hasInternalDataProvider() )
841             SchXMLTableHelper::applyTableToInternalDataProvider( maTable, xNewDoc );
842 
843         bool bOlderThan2_3 = SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( xNewDoc );
844         bool bOldFileWithOwnDataFromRows = (bOlderThan2_3 && bHasOwnData && (meDataRowSource==chart::ChartDataRowSource_ROWS)); // in this case there are range addresses that are simply wrong.
845 
846         if( mbAllRangeAddressesAvailable && !bSpecialHandlingForDonutChart && !mbIsStockChart &&
847             !bOldFileWithOwnDataFromRows )
848         {
849             //bHasOwnData is true in this case!
850             //e.g. for normal files with own data or also in case of copy paste scenario (e.g. calc to impress)
851             bSwitchRangesFromOuterToInternalIfNecessary = true;
852         }
853         else
854         {
855             //apply data from rectangular range
856 
857             // create datasource from data provider with rectangular range parameters and change the diagram setDiagramData
858             try
859             {
860                 if( bOlderThan2_3 && xDiaProp.is() )//for older charts the hidden cells were removed by calc on the fly
861                     xDiaProp->setPropertyValue("IncludeHiddenCells",uno::Any(false));
862 
863                 // note: mbRowHasLabels means the first row contains labels, that means we have "column-descriptions",
864                 // (analogously mbColHasLabels means we have "row-descriptions")
865                 lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans );
866             }
867             catch(const uno::Exception&)
868             {
869                 //try to fallback to internal data
870                 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram try to fallback to internal data" );
871                 if(!bHasOwnData)
872                 {
873                     bHasOwnData = true;
874                     msChartAddress = "all";
875                     if( !xNewDoc->hasInternalDataProvider() )
876                     {
877                         xNewDoc->createInternalDataProvider( false /* bCloneExistingData */ );
878                         SchXMLTableHelper::applyTableToInternalDataProvider( maTable, xNewDoc );
879                         try
880                         {
881                             lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans );
882                         }
883                         catch(const uno::Exception&)
884                         {
885                             TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram fallback to internal data failed also" );
886                         }
887                     }
888                 }
889             }
890         }
891     }
892     else
893     {
894         SAL_WARN("xmloff.chart", "Must not get here" );
895     }
896 
897     // now all series and data point properties are available and can be set
898     {
899         if( bSpecialHandlingForDonutChart )
900         {
901             uno::Reference< chart2::XDiagram > xNewDiagram( xNewDoc->getFirstDiagram() );
902             lcl_swapPointAndSeriesStylesForDonutCharts( maSeriesDefaultsAndStyles.maSeriesStyleVector
903                 , SchXMLSeriesHelper::getDataSeriesIndexMapFromDiagram(xNewDiagram) );
904         }
905 
906         SchXMLSeries2Context::initSeriesPropertySets( maSeriesDefaultsAndStyles, xDoc );
907 
908         //set defaults from diagram to the new series:
909         //check whether we need to remove lines from symbol only charts
910         bool bSwitchOffLinesForScatter = false;
911         {
912             bool bLinesOn = true;
913             if( (maSeriesDefaultsAndStyles.maLinesOnProperty >>= bLinesOn) && !bLinesOn )
914             {
915                 if( maChartTypeServiceName == "com.sun.star.chart2.ScatterChartType" )
916                 {
917                     bSwitchOffLinesForScatter = true;
918                     SchXMLSeries2Context::switchSeriesLinesOff( maSeriesDefaultsAndStyles.maSeriesStyleVector );
919                 }
920             }
921         }
922         SchXMLSeries2Context::setDefaultsToSeries( maSeriesDefaultsAndStyles );
923 
924         // set autostyles for series and data points
925         const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext();
926         const SvXMLStyleContext* pStyle = nullptr;
927         OUString sCurrStyleName;
928 
929         if( pStylesCtxt )
930         {
931             //iterate over data-series first
932             //don't set series styles for donut charts
933             if( !bSpecialHandlingForDonutChart )
934             {
935                 SchXMLSeries2Context::setStylesToSeries(
936                                         maSeriesDefaultsAndStyles, pStylesCtxt, pStyle,
937                                         sCurrStyleName, mrImportHelper, GetImport(),
938                                         mbIsStockChart, maLSequencesPerIndex );
939                 // ... then set attributes for statistics (after their existence was set in the series)
940                 SchXMLSeries2Context::setStylesToStatisticsObjects(
941                                         maSeriesDefaultsAndStyles, pStylesCtxt,
942                                         pStyle, sCurrStyleName );
943 
944                 SchXMLSeries2Context::setStylesToRegressionCurves(
945                                         maSeriesDefaultsAndStyles, pStylesCtxt,
946                                         pStyle, sCurrStyleName );
947             }
948         }
949 
950         //#i98319# call switchRangesFromOuterToInternalIfNecessary before the data point styles are applied, otherwise in copy->paste scenario the data point styles do get lost
951         if( bSwitchRangesFromOuterToInternalIfNecessary )
952         {
953             if( xNewDoc->hasInternalDataProvider() )
954                 SchXMLTableHelper::switchRangesFromOuterToInternalIfNecessary( maTable, maLSequencesPerIndex, xNewDoc, meDataRowSource );
955         }
956 
957         if( pStylesCtxt )
958         {
959             // ... then iterate over data-point attributes, so the latter are not overwritten
960             SchXMLSeries2Context::setStylesToDataPoints( maSeriesDefaultsAndStyles
961                             , pStylesCtxt, pStyle, sCurrStyleName, mrImportHelper, GetImport(), mbIsStockChart, bSpecialHandlingForDonutChart, bSwitchOffLinesForScatter );
962         }
963     }
964 
965     if( xProp.is())
966         xProp->setPropertyValue("RefreshAddInAllowed", uno::Any( true) );
967 }
968 
969 void SchXMLChartContext::MergeSeriesForStockChart()
970 {
971     OSL_ASSERT( mbIsStockChart );
972     try
973     {
974         uno::Reference< chart::XChartDocument > xOldDoc( mrImportHelper.GetChartDocument());
975         uno::Reference< chart2::XChartDocument > xDoc( xOldDoc, uno::UNO_QUERY_THROW );
976         uno::Reference< chart2::XDiagram > xDiagram( xDoc->getFirstDiagram());
977         if( ! xDiagram.is())
978             return;
979 
980         bool bHasJapaneseCandlestick = true;
981         uno::Reference< chart2::XDataSeriesContainer > xDSContainer;
982         uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW );
983         const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
984         for( const auto& rCooSys : aCooSysSeq )
985         {
986             uno::Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY_THROW );
987             const uno::Sequence< uno::Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes());
988             auto pChartType = std::find_if(aChartTypes.begin(), aChartTypes.end(),
989                 [](const auto& rChartType) { return rChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType"; });
990             if (pChartType != aChartTypes.end())
991             {
992                 xDSContainer.set( *pChartType, uno::UNO_QUERY_THROW );
993                 uno::Reference< beans::XPropertySet > xCTProp( *pChartType, uno::UNO_QUERY_THROW );
994                 xCTProp->getPropertyValue("Japanese") >>= bHasJapaneseCandlestick;
995             }
996         }
997 
998         if( xDSContainer.is())
999         {
1000             // with japanese candlesticks: open, low, high, close
1001             // otherwise: low, high, close
1002             uno::Sequence< uno::Reference< chart2::XDataSeries > > aSeriesSeq( xDSContainer->getDataSeries());
1003             const sal_Int32 nSeriesCount( aSeriesSeq.getLength());
1004             const sal_Int32 nSeriesPerCandleStick = bHasJapaneseCandlestick ? 4: 3;
1005             sal_Int32 nCandleStickCount = nSeriesCount / nSeriesPerCandleStick;
1006             OSL_ASSERT( nSeriesPerCandleStick * nCandleStickCount == nSeriesCount );
1007             uno::Sequence< uno::Reference< chart2::XDataSeries > > aNewSeries( nCandleStickCount );
1008             auto aNewSeriesRange = asNonConstRange(aNewSeries);
1009             for( sal_Int32 i=0; i<nCandleStickCount; ++i )
1010             {
1011                 sal_Int32 nSeriesIndex = i*nSeriesPerCandleStick;
1012                 if( bHasJapaneseCandlestick )
1013                 {
1014                     // open values
1015                     lcl_setRoleAtFirstSequence( aSeriesSeq[ nSeriesIndex ], "values-first");
1016                     aNewSeriesRange[i] = aSeriesSeq[ nSeriesIndex ];
1017                     // low values
1018                     lcl_MoveDataToCandleStickSeries(
1019                         uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ),
1020                         aNewSeries[i], "values-min");
1021                 }
1022                 else
1023                 {
1024                     // low values
1025                     lcl_setRoleAtFirstSequence( aSeriesSeq[ nSeriesIndex ], "values-min");
1026                     aNewSeriesRange[i] = aSeriesSeq[ nSeriesIndex ];
1027                 }
1028                 // high values
1029                 lcl_MoveDataToCandleStickSeries(
1030                     uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ),
1031                     aNewSeries[i], "values-max");
1032                 // close values
1033                 lcl_MoveDataToCandleStickSeries(
1034                     uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ),
1035                     aNewSeries[i], "values-last");
1036             }
1037             xDSContainer->setDataSeries( aNewSeries );
1038         }
1039     }
1040     catch(const uno::Exception&)
1041     {
1042         TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception while merging series for stock chart" );
1043     }
1044 }
1045 
1046 css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLChartContext::createFastChildContext(
1047     sal_Int32 nElement,
1048     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
1049 {
1050     SvXMLImportContext* pContext = nullptr;
1051     uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument();
1052     uno::Reference< beans::XPropertySet > xProp( xDoc, uno::UNO_QUERY );
1053 
1054     switch(nElement)
1055     {
1056         case XML_ELEMENT(CHART, XML_PLOT_AREA):
1057             pContext = new SchXMLPlotAreaContext( mrImportHelper, GetImport(),
1058                                                   m_aXLinkHRefAttributeToIndicateDataProvider,
1059                                                   msCategoriesAddress,
1060                                                   msChartAddress, m_bHasRangeAtPlotArea, mbAllRangeAddressesAvailable,
1061                                                   mbColHasLabels, mbRowHasLabels,
1062                                                   meDataRowSource,
1063                                                   maSeriesDefaultsAndStyles,
1064                                                   maChartTypeServiceName,
1065                                                   maLSequencesPerIndex, maChartSize );
1066             break;
1067         case XML_ELEMENT(CHART, XML_TITLE):
1068             if( xDoc.is())
1069             {
1070                 if( xProp.is())
1071                 {
1072                     xProp->setPropertyValue("HasMainTitle", uno::Any(true) );
1073                 }
1074                 pContext = new SchXMLTitleContext( mrImportHelper, GetImport(),
1075                                                    maMainTitle, xDoc->getTitle() );
1076             }
1077             break;
1078         case XML_ELEMENT(CHART, XML_SUBTITLE):
1079             if( xDoc.is())
1080             {
1081                 if( xProp.is())
1082                 {
1083                     xProp->setPropertyValue("HasSubTitle", uno::Any(true) );
1084                 }
1085                 pContext = new SchXMLTitleContext( mrImportHelper, GetImport(),
1086                                                    maSubTitle, xDoc->getSubTitle() );
1087             }
1088             break;
1089         case XML_ELEMENT(CHART, XML_LEGEND):
1090             pContext = new SchXMLLegendContext( mrImportHelper, GetImport() );
1091             break;
1092         case XML_ELEMENT(LO_EXT, XML_DATA_TABLE):
1093             pContext = new SchXMLDataTableContext(mrImportHelper, GetImport());
1094             break;
1095         case XML_ELEMENT(TABLE, XML_TABLE):
1096             {
1097                 SchXMLTableContext * pTableContext =
1098                     new SchXMLTableContext( GetImport(), maTable );
1099                 m_bHasTableElement = true;
1100                 // #i85913# take into account column- and row- mapping for
1101                 // charts with own data only for those which were not copied
1102                 // from a place where they got data from the container.  Note,
1103                 // that this requires the plot-area been read before the table
1104                 // (which is required in the ODF spec)
1105                 // Note: For stock charts and donut charts with special handling
1106                 // the mapping must not be applied!
1107                 if( msChartAddress.isEmpty() && !mbIsStockChart &&
1108                     !lcl_SpecialHandlingForDonutChartNeeded(
1109                         maChartTypeServiceName, GetImport()))
1110                 {
1111                     if( !msColTrans.isEmpty() )
1112                     {
1113                         OSL_ASSERT( msRowTrans.isEmpty() );
1114                         pTableContext->setColumnPermutation( lcl_getNumberSequenceFromString( msColTrans, true ));
1115                         msColTrans.clear();
1116                     }
1117                     else if( !msRowTrans.isEmpty() )
1118                     {
1119                         pTableContext->setRowPermutation( lcl_getNumberSequenceFromString( msRowTrans, true ));
1120                         msRowTrans.clear();
1121                     }
1122                 }
1123                 pContext = pTableContext;
1124             }
1125             break;
1126 
1127         default:
1128             // try importing as an additional shape
1129             if( ! mxDrawPage.is())
1130             {
1131                 uno::Reference< drawing::XDrawPageSupplier  > xSupp( xDoc, uno::UNO_QUERY );
1132                 if( xSupp.is())
1133                     mxDrawPage = xSupp->getDrawPage();
1134 
1135                 SAL_WARN_IF( !mxDrawPage.is(), "xmloff.chart", "Invalid Chart Page" );
1136             }
1137             if( mxDrawPage.is())
1138                 pContext = XMLShapeImportHelper::CreateGroupChildContext(
1139                     GetImport(), nElement, xAttrList, mxDrawPage );
1140             break;
1141     }
1142 
1143     return pContext;
1144 }
1145 
1146 /*
1147     With a locked controller the following is done here:
1148         1.  Hide title, subtitle, and legend.
1149         2.  Set the size of the draw page.
1150         3.  Set a (logically) empty data set.
1151         4.  Set the chart type.
1152 */
1153 void SchXMLChartContext::InitChart(
1154     const OUString & rChartTypeServiceName // currently the old service name
1155     )
1156 {
1157     uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument();
1158     SAL_WARN_IF( !xDoc.is(), "xmloff.chart", "No valid document!" );
1159 
1160     // Remove Title and Diagram ("De-InitNew")
1161     uno::Reference< chart2::XChartDocument > xNewDoc( mrImportHelper.GetChartDocument(), uno::UNO_QUERY );
1162     if( xNewDoc.is())
1163     {
1164         xNewDoc->setFirstDiagram( nullptr );
1165         uno::Reference< chart2::XTitled > xTitled( xNewDoc, uno::UNO_QUERY );
1166         if( xTitled.is())
1167             xTitled->setTitleObject( nullptr );
1168     }
1169 
1170     //  Set the chart type via setting the diagram.
1171     if( !rChartTypeServiceName.isEmpty() && xDoc.is())
1172     {
1173         uno::Reference< lang::XMultiServiceFactory > xFact( xDoc, uno::UNO_QUERY );
1174         if( xFact.is())
1175         {
1176             uno::Reference< chart::XDiagram > xDia( xFact->createInstance( rChartTypeServiceName ), uno::UNO_QUERY );
1177             if( xDia.is())
1178                 xDoc->setDiagram( xDia );
1179         }
1180     }
1181 }
1182 
1183 SchXMLTitleContext::SchXMLTitleContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport,
1184                                         OUString& rTitle,
1185                                         uno::Reference< drawing::XShape > xTitleShape ) :
1186         SvXMLImportContext( rImport ),
1187         mrImportHelper( rImpHelper ),
1188         mrTitle( rTitle ),
1189         mxTitleShape(std::move( xTitleShape ))
1190 {
1191 }
1192 
1193 SchXMLTitleContext::~SchXMLTitleContext()
1194 {}
1195 
1196 void SchXMLTitleContext::startFastElement( sal_Int32 /*nElement*/,
1197     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
1198 {
1199     css::awt::Point aPosition;
1200     bool bHasXPosition=false;
1201     bool bHasYPosition=false;
1202 
1203     for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
1204     {
1205         switch (aIter.getToken())
1206         {
1207             case XML_ELEMENT(SVG, XML_X):
1208             case XML_ELEMENT(SVG_COMPAT, XML_X):
1209             {
1210                 GetImport().GetMM100UnitConverter().convertMeasureToCore(
1211                         aPosition.X, aIter.toView() );
1212                 bHasXPosition = true;
1213                 break;
1214             }
1215             case XML_ELEMENT(SVG, XML_Y):
1216             case XML_ELEMENT(SVG_COMPAT, XML_Y):
1217             {
1218                 GetImport().GetMM100UnitConverter().convertMeasureToCore(
1219                         aPosition.Y, aIter.toView() );
1220                 bHasYPosition = true;
1221                 break;
1222             }
1223             case XML_ELEMENT(CHART, XML_STYLE_NAME):
1224                 msAutoStyleName = aIter.toString();
1225                 break;
1226             default:
1227                 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
1228         }
1229     }
1230 
1231     if( mxTitleShape.is())
1232     {
1233         if( bHasXPosition && bHasYPosition )
1234             mxTitleShape->setPosition( aPosition );
1235 
1236         uno::Reference<beans::XPropertySet> xProp(mxTitleShape, uno::UNO_QUERY);
1237         mrImportHelper.FillAutoStyle(msAutoStyleName, xProp);
1238     }
1239 }
1240 
1241 css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTitleContext::createFastChildContext(
1242     sal_Int32 nElement,
1243     const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
1244 {
1245     SvXMLImportContext* pContext = nullptr;
1246 
1247     if( nElement == XML_ELEMENT(TEXT, XML_P) ||
1248         nElement == XML_ELEMENT(LO_EXT, XML_P) )
1249     {
1250         pContext = new SchXMLParagraphContext( GetImport(), mrTitle );
1251     }
1252     else
1253         XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
1254 
1255     return pContext;
1256 }
1257 
1258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1259