1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmloff.hxx"
30 
31 #include "SchXMLTools.hxx"
32 
33 /*
34 #include <tools/debug.hxx>
35 */
36 #include <rtl/ustrbuf.hxx>
37 #include <comphelper/InlineContainer.hxx>
38 // header for class SvXMLUnitConverter
39 #include <xmloff/xmluconv.hxx>
40 // header for struct SvXMLEnumMapEntry
41 #include <xmloff/xmlement.hxx>
42 // header for define __FAR_DATA
43 #include <tools/solar.h>
44 
45 // header for class SvXMLImportPropertyMapper
46 #include <xmloff/xmlimppr.hxx>
47 // header for class XMLPropStyleContext
48 #include <xmloff/prstylei.hxx>
49 // header for class XMLPropertySetMapper
50 #include <xmloff/xmlprmap.hxx>
51 #include <xmloff/xmlexp.hxx>
52 #include "xmloff/xmlnmspe.hxx"
53 #include <xmloff/xmlmetai.hxx>
54 
55 #include <com/sun/star/beans/PropertyAttribute.hpp>
56 #include <com/sun/star/uno/XComponentContext.hpp>
57 #include <com/sun/star/chart2/data/XDataProvider.hpp>
58 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
59 #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
60 #include <com/sun/star/chart2/XChartDocument.hpp>
61 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
62 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
63 #include <com/sun/star/container/XChild.hpp>
64 #include <com/sun/star/document/XDocumentProperties.hpp>
65 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
66 #include <com/sun/star/lang/XServiceName.hpp>
67 
68 #include <comphelper/processfactory.hxx>
69 
70 using namespace com::sun::star;
71 using namespace ::xmloff::token;
72 
73 using ::rtl::OUString;
74 using ::rtl::OUStringBuffer;
75 using ::com::sun::star::uno::Reference;
76 using ::com::sun::star::uno::Sequence;
77 
78 namespace
79 {
80 Reference< uno::XComponentContext > lcl_getComponentContext()
81 {
82     Reference< uno::XComponentContext > xContext;
83     try
84     {
85         Reference< beans::XPropertySet > xFactProp( comphelper::getProcessServiceFactory(), uno::UNO_QUERY );
86         if( xFactProp.is())
87             xFactProp->getPropertyValue(OUString::createFromAscii("DefaultContext")) >>= xContext;
88     }
89     catch( uno::Exception& )
90     {}
91 
92     return xContext;
93 }
94 
95 rtl::OUString lcl_getGeneratorFromModel( const uno::Reference< frame::XModel >& xChartModel )
96 {
97     ::rtl::OUString aGenerator;
98     uno::Reference< document::XDocumentPropertiesSupplier> xChartDocumentPropertiesSupplier( xChartModel, uno::UNO_QUERY );
99     if( xChartDocumentPropertiesSupplier.is() )
100     {
101         uno::Reference< document::XDocumentProperties > xChartDocumentProperties(
102             xChartDocumentPropertiesSupplier->getDocumentProperties());
103         if( xChartDocumentProperties.is() )
104             aGenerator =  xChartDocumentProperties->getGenerator();
105     }
106     return aGenerator;
107 }
108 
109 rtl::OUString lcl_getGeneratorFromModelOrItsParent( const uno::Reference< frame::XModel >& xChartModel )
110 {
111     ::rtl::OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
112     if( !aGenerator.getLength() ) //try to get the missing info from the parent document
113     {
114         uno::Reference< container::XChild > xChild( xChartModel, uno::UNO_QUERY );
115         if( xChild.is() )
116             aGenerator = lcl_getGeneratorFromModel( uno::Reference< frame::XModel >( xChild->getParent(), uno::UNO_QUERY) );
117     }
118     return aGenerator;
119 }
120 
121 sal_Int32 lcl_getBuildIDFromGenerator( const ::rtl::OUString& rGenerator )
122 {
123     //returns -1 if nothing found
124     sal_Int32 nBuildId = -1;
125     const OUString sBuildCompare( RTL_CONSTASCII_USTRINGPARAM( "$Build-" ) );
126     sal_Int32 nEnd = -1;
127     sal_Int32 nBegin = rGenerator.indexOf( sBuildCompare, nEnd );
128     if( nBegin != -1 )
129     {
130         OUString sBuildId( rGenerator.copy( nBegin + sBuildCompare.getLength() ) );
131         nBuildId = sBuildId.toInt32();
132     }
133     return nBuildId;
134 }
135 
136 OUString lcl_ConvertRange( const ::rtl::OUString & rRange, const Reference< chart2::data::XDataProvider >& xDataProvider )
137 {
138     OUString aResult = rRange;
139     Reference< chart2::data::XRangeXMLConversion > xRangeConversion( xDataProvider, uno::UNO_QUERY );
140     if( xRangeConversion.is())
141         aResult = xRangeConversion->convertRangeFromXML( rRange );
142     return aResult;
143 }
144 
145 Reference< chart2::data::XDataSequence > lcl_createNewSequenceFromCachedXMLRange( const Reference< chart2::data::XDataSequence >& xSeq, const Reference< chart2::data::XDataProvider >& xDataProvider )
146 {
147     Reference< chart2::data::XDataSequence > xRet;
148     OUString aRange;
149     if( xSeq.is() && SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq, aRange, /* bClearProp = */ true ) )
150     {
151         xRet.set( xDataProvider->createDataSequenceByRangeRepresentation(
152             lcl_ConvertRange( aRange, xDataProvider )) );
153         SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ),
154             Reference< beans::XPropertySet >( xRet, uno::UNO_QUERY ));
155     }
156     return xRet;
157 }
158 
159 } // anonymous namespace
160 
161 // ----------------------------------------
162 
163 namespace SchXMLTools
164 {
165 
166 static __FAR_DATA SvXMLEnumMapEntry aXMLChartClassMap[] =
167 {
168 	{ XML_LINE,	    	XML_CHART_CLASS_LINE	},
169 	{ XML_AREA,		    XML_CHART_CLASS_AREA	},
170 	{ XML_CIRCLE,		XML_CHART_CLASS_CIRCLE	},
171 	{ XML_RING,		    XML_CHART_CLASS_RING	},
172 	{ XML_SCATTER,		XML_CHART_CLASS_SCATTER	},
173 	{ XML_RADAR,		XML_CHART_CLASS_RADAR	},
174     { XML_FILLED_RADAR,	XML_CHART_CLASS_FILLED_RADAR },
175 	{ XML_BAR,			XML_CHART_CLASS_BAR		},
176 	{ XML_STOCK,		XML_CHART_CLASS_STOCK	},
177 	{ XML_BUBBLE,		XML_CHART_CLASS_BUBBLE	},
178     { XML_SURFACE,		XML_CHART_CLASS_BAR	    }, //@todo change this if a surface chart is available
179     { XML_ADD_IN,       XML_CHART_CLASS_ADDIN   },
180 	{ XML_TOKEN_INVALID, XML_CHART_CLASS_UNKNOWN }
181 };
182 
183 SchXMLChartTypeEnum GetChartTypeEnum( const OUString& rClassName )
184 {
185     sal_uInt16 nEnumVal = XML_CHART_CLASS_UNKNOWN;
186     if( !SvXMLUnitConverter::convertEnum(
187 									nEnumVal, rClassName, aXMLChartClassMap ) )
188         nEnumVal = XML_CHART_CLASS_UNKNOWN;
189     return SchXMLChartTypeEnum(nEnumVal);
190 }
191 
192 typedef ::comphelper::MakeMap< ::rtl::OUString, ::rtl::OUString > tMakeStringStringMap;
193 //static
194 const tMakeStringStringMap& lcl_getChartTypeNameMap()
195 {
196     //shape property -- chart model object property
197     static tMakeStringStringMap g_aChartTypeNameMap =
198         tMakeStringStringMap
199         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.LineDiagram" )
200         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.LineChartType" ) )
201 
202         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.AreaDiagram" )
203         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.AreaChartType" ) )
204 
205         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.BarDiagram" )
206         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.ColumnChartType" ) )
207 
208         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.PieDiagram" )
209         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.PieChartType" ) )
210 
211         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.DonutDiagram" )
212         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.DonutChartType" ) )
213 
214         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.XYDiagram" )
215         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.ScatterChartType" ) )
216 
217         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.NetDiagram" )
218         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.NetChartType" ) )
219 
220         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.FilledNetDiagram" )
221         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.FilledNetChartType" ) )
222 
223         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.StockDiagram" )
224         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.CandleStickChartType" ) )
225 
226         ( ::rtl::OUString::createFromAscii( "com.sun.star.chart.BubbleDiagram" )
227         , ::rtl::OUString::createFromAscii( "com.sun.star.chart2.BubbleChartType" ) )
228 
229         ;
230     return g_aChartTypeNameMap;
231 }
232 
233 
234 OUString GetNewChartTypeName( const OUString & rOldChartTypeName )
235 {
236     OUString aNew(rOldChartTypeName);
237 
238     const tMakeStringStringMap& rMap = lcl_getChartTypeNameMap();
239     tMakeStringStringMap::const_iterator aIt( rMap.find( rOldChartTypeName ));
240     if( aIt != rMap.end())
241     {
242         aNew = aIt->second;
243     }
244     return aNew;
245 }
246 
247 OUString GetChartTypeByClassName(
248     const OUString & rClassName, bool bUseOldNames )
249 {
250     OUStringBuffer aResultBuffer;
251     bool bInternalType = false;
252 
253     if( bUseOldNames )
254         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("com.sun.star.chart."));
255     else
256         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("com.sun.star.chart2."));
257 
258     bInternalType = true;
259 
260     if( IsXMLToken( rClassName, XML_LINE ))
261         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Line"));
262     else if( IsXMLToken( rClassName, XML_AREA ))
263         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Area"));
264     else if( IsXMLToken( rClassName, XML_BAR ))
265     {
266         if( bUseOldNames )
267             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Bar"));
268         else
269         {
270             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Column"));
271             // @todo: might be Bar
272         }
273     }
274     else if( IsXMLToken( rClassName, XML_CIRCLE ))
275         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Pie"));
276     else if( IsXMLToken( rClassName, XML_RING ))
277         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Donut"));
278     else if( IsXMLToken( rClassName, XML_SCATTER ))
279     {
280         if( bUseOldNames )
281             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("XY"));
282         else
283             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Scatter"));
284     }
285 
286     else if( IsXMLToken( rClassName, XML_BUBBLE ))
287         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Bubble"));
288     else if( IsXMLToken( rClassName, XML_RADAR ))
289         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Net"));
290     else if( IsXMLToken( rClassName, XML_FILLED_RADAR ))
291         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("FilledNet"));
292     else if( IsXMLToken( rClassName, XML_STOCK ))
293     {
294         if( bUseOldNames )
295             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Stock"));
296         else
297             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("CandleStick"));
298     }
299     else if( IsXMLToken( rClassName, XML_SURFACE ))
300     {
301         //@todo change this if a surface chart is available
302         if( bUseOldNames )
303             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Bar"));
304         else
305             aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Column"));
306     }
307     else
308         bInternalType = false;
309 
310     if( ! bInternalType )
311         return OUString();
312 
313     if( bUseOldNames )
314         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("Diagram"));
315     else
316         aResultBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM("ChartType"));
317 
318     return aResultBuffer.makeStringAndClear();
319 
320 }
321 
322 XMLTokenEnum getTokenByChartType(
323     const OUString & rChartTypeService, bool bUseOldNames )
324 {
325     XMLTokenEnum eResult = XML_TOKEN_INVALID;
326     OUString aPrefix, aPostfix;
327 
328     if( bUseOldNames )
329     {
330         aPrefix = OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.chart."));
331         aPostfix = OUString( RTL_CONSTASCII_USTRINGPARAM("Diagram"));
332     }
333     else
334     {
335         aPrefix = OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.chart2."));
336         aPostfix = OUString( RTL_CONSTASCII_USTRINGPARAM("ChartType"));
337     }
338 
339     if( rChartTypeService.match( aPrefix ))
340     {
341         sal_Int32 nSkip = aPrefix.getLength();
342         OSL_ASSERT( rChartTypeService.getLength() >= nSkip );
343         sal_Int32 nTypeLength = rChartTypeService.getLength() - nSkip - aPostfix.getLength();
344         // if postfix matches and leaves a non-empty type
345         if( nTypeLength > 0 && rChartTypeService.match( aPostfix, nSkip + nTypeLength ))
346         {
347             OUString aServiceName( rChartTypeService.copy( nSkip, nTypeLength ));
348 
349             if( aServiceName.equalsAscii("Line"))
350                 eResult = XML_LINE;
351             else if( aServiceName.equalsAscii("Area"))
352                 eResult = XML_AREA;
353             else if( aServiceName.equalsAscii("Bar") ||
354                      (!bUseOldNames && aServiceName.equalsAscii("Column")))
355                 eResult = XML_BAR;
356             else if( aServiceName.equalsAscii("Pie"))
357                 eResult = XML_CIRCLE;
358             else if( aServiceName.equalsAscii("Donut"))
359                 eResult = XML_RING;
360             else if( (bUseOldNames && aServiceName.equalsAscii("XY")) ||
361                      (!bUseOldNames && aServiceName.equalsAscii("Scatter")))
362                 eResult = XML_SCATTER;
363             else if( aServiceName.equalsAscii("Bubble"))
364                 eResult = XML_BUBBLE;
365             else if( aServiceName.equalsAscii("Net"))
366                 eResult = XML_RADAR;
367             else if( aServiceName.equalsAscii("FilledNet"))
368                 eResult = XML_FILLED_RADAR;
369             else if( (bUseOldNames && aServiceName.equalsAscii("Stock")) ||
370                      (!bUseOldNames && aServiceName.equalsAscii("CandleStick")))
371                 eResult = XML_STOCK;
372         }
373     }
374 
375     if( eResult == XML_TOKEN_INVALID && rChartTypeService.getLength() > 0 )
376         eResult = XML_ADD_IN;
377 
378     return eResult;
379 }
380 
381 Reference< chart2::data::XLabeledDataSequence > GetNewLabeledDataSequence()
382 {
383     Reference< chart2::data::XLabeledDataSequence >  xResult;
384     Reference< uno::XComponentContext > xContext( lcl_getComponentContext());
385     if( xContext.is() )
386         xResult.set(
387             xContext->getServiceManager()->createInstanceWithContext(
388                 OUString::createFromAscii("com.sun.star.chart2.data.LabeledDataSequence"),
389                 xContext ), uno::UNO_QUERY_THROW );
390     return xResult;
391 }
392 
393 Reference< chart2::data::XDataSequence > CreateDataSequence(
394         const OUString & rRange,
395         const Reference< chart2::XChartDocument >& xChartDoc )
396 {
397     Reference< chart2::data::XDataSequence > xRet;
398 
399     if( !xChartDoc.is() )
400     {
401         DBG_ERROR( "need a chart document" );
402         return xRet;
403     }
404 
405     Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
406     if( !xDataProvider.is() )
407     {
408         DBG_ERROR( "need a data provider" );
409         return xRet;
410     }
411 
412     try
413     {
414         xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( lcl_ConvertRange( rRange, xDataProvider )));
415         SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange );
416     }
417     catch( const lang::IllegalArgumentException & )
418     {
419         DBG_ERROR( "could not create data sequence" );
420     }
421 
422     if( !xRet.is() && !xChartDoc->hasInternalDataProvider() && rRange.getLength() )
423     {
424         //#i103911# switch to internal data in case the parent cannot provide the requested data
425         xChartDoc->createInternalDataProvider( sal_True /* bCloneExistingData */ );
426         xDataProvider = xChartDoc->getDataProvider();
427         try
428         {
429             xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( lcl_ConvertRange( rRange, xDataProvider )));
430             SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange );
431         }
432         catch( const lang::IllegalArgumentException & )
433         {
434             DBG_ERROR( "could not create data sequence" );
435         }
436     }
437     return xRet;
438 }
439 
440 void CreateCategories(
441     const uno::Reference< chart2::data::XDataProvider > & xDataProvider,
442     const uno::Reference< chart2::XChartDocument > & xNewDoc,
443     const OUString & rRangeAddress,
444     sal_Int32 nCooSysIndex,
445     sal_Int32 nDimensionIndex,
446     tSchXMLLSequencesPerIndex * pLSequencesPerIndex )
447 {
448     try
449     {
450         if( xNewDoc.is() && rRangeAddress.getLength())
451         {
452             if( xDataProvider.is())
453             {
454                 uno::Reference< chart2::XDiagram > xDia( xNewDoc->getFirstDiagram());
455                 if( !xDia.is())
456                     return;
457 
458                 uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDia, uno::UNO_QUERY_THROW );
459                 uno::Sequence< uno::Reference< chart2::XCoordinateSystem > >
460                     aCooSysSeq( xCooSysCnt->getCoordinateSystems());
461                 if( nCooSysIndex < aCooSysSeq.getLength())
462                 {
463                     uno::Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIndex] );
464                     OSL_ASSERT( xCooSys.is());
465                     if( nDimensionIndex < xCooSys->getDimension() )
466                     {
467                         const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
468                         for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
469                         {
470                             uno::Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( nDimensionIndex, nI ));
471                             if( xAxis.is() )
472                             {
473                                 chart2::ScaleData aData( xAxis->getScaleData());
474                                 uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
475                                     GetNewLabeledDataSequence());
476                                 try
477                                 {
478                                     OUString aConvertedRange( rRangeAddress );
479                                     bool bRangeConverted = false;
480                                     if( ! (xNewDoc->hasInternalDataProvider() &&
481                                            aConvertedRange.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("categories"))))
482                                     {
483                                         Reference< chart2::data::XRangeXMLConversion > xXMLConv( xDataProvider, uno::UNO_QUERY );
484                                         if( xXMLConv.is())
485                                         {
486                                             aConvertedRange = xXMLConv->convertRangeFromXML( rRangeAddress );
487                                             bRangeConverted = true;
488                                         }
489                                     }
490                                     Reference< chart2::data::XDataSequence > xSeq(
491                                         xDataProvider->createDataSequenceByRangeRepresentation( aConvertedRange ));
492                                     xLabeledSeq->setValues( xSeq );
493                                     if( bRangeConverted )
494                                         setXMLRangePropertyAtDataSequence( xSeq, rRangeAddress );
495                                 }
496                                 catch( const lang::IllegalArgumentException & ex )
497                                 {
498                                     (void)ex; // avoid warning for pro build
499                                     OSL_ENSURE( false, ::rtl::OUStringToOString(
500                                                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IllegalArgumentException caught, Message: " )) +
501                                                     ex.Message, RTL_TEXTENCODING_ASCII_US ).getStr());
502                                 }
503                                 aData.Categories.set( xLabeledSeq );
504                                 if( pLSequencesPerIndex )
505                                 {
506                                     // register for setting local data if external data provider is not present
507                                     pLSequencesPerIndex->insert(
508                                         tSchXMLLSequencesPerIndex::value_type(
509                                             tSchXMLIndexWithPart( SCH_XML_CATEGORIES_INDEX, SCH_XML_PART_VALUES ), xLabeledSeq ));
510                                 }
511                                 xAxis->setScaleData( aData );
512                             }
513                         }
514                     }
515                 }
516             }
517         }
518     }
519     catch( uno::Exception & )
520     {
521         OSL_ENSURE( false, "Exception caught while creating Categories" );
522     }
523 }
524 
525 
526 uno::Any getPropertyFromContext( const rtl::OUString& rPropertyName, const XMLPropStyleContext* pPropStyleContext, const SvXMLStylesContext* pStylesCtxt )
527 {
528     uno::Any aRet;
529     if( !pPropStyleContext || !pStylesCtxt )
530         return aRet;
531     const ::std::vector< XMLPropertyState >& rProperties = pPropStyleContext->GetProperties();
532     const UniReference< XMLPropertySetMapper >& rMapper = pStylesCtxt->GetImportPropertyMapper( pPropStyleContext->GetFamily()/*XML_STYLE_FAMILY_SCH_CHART_ID*/ )->getPropertySetMapper();
533     ::std::vector< XMLPropertyState >::const_iterator aEnd( rProperties.end() );
534     ::std::vector< XMLPropertyState >::const_iterator aPropIter( rProperties.begin() );
535     for( aPropIter = rProperties.begin(); aPropIter != aEnd; ++aPropIter )
536     {
537         sal_Int32 nIdx = aPropIter->mnIndex;
538         if( nIdx == -1 )
539             continue;
540         OUString aPropName = rMapper->GetEntryAPIName( nIdx );
541         if(rPropertyName.equals(aPropName))
542             return aPropIter->maValue;
543     }
544     return aRet;
545 }
546 
547 void exportText( SvXMLExport& rExport, const OUString& rText, bool bConvertTabsLFs )
548 {
549     SvXMLElementExport aPara( rExport, XML_NAMESPACE_TEXT,
550                               ::xmloff::token::GetXMLToken( ::xmloff::token::XML_P ),
551                               sal_True, sal_False );
552 
553     if( bConvertTabsLFs )
554     {
555         sal_Int32 nStartPos = 0;
556         sal_Int32 nEndPos = rText.getLength();
557         sal_Unicode cChar;
558 
559         for( sal_Int32 nPos = 0; nPos < nEndPos; nPos++ )
560         {
561             cChar = rText[ nPos ];
562             switch( cChar )
563             {
564                 case 0x0009:        // tabulator
565                     {
566                         if( nPos > nStartPos )
567                             rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) );
568                         nStartPos = nPos + 1;
569 
570                         SvXMLElementExport aElem( rExport, XML_NAMESPACE_TEXT,
571                                                   ::xmloff::token::GetXMLToken( ::xmloff::token::XML_TAB_STOP ),
572                                                   sal_False, sal_False );
573                     }
574                     break;
575 
576                 case 0x000A:        // linefeed
577                     {
578                         if( nPos > nStartPos )
579                             rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) );
580                         nStartPos = nPos + 1;
581 
582                         SvXMLElementExport aElem( rExport, XML_NAMESPACE_TEXT,
583                                                   ::xmloff::token::GetXMLToken( ::xmloff::token::XML_LINE_BREAK ),
584                                                   sal_False, sal_False );
585                     }
586                     break;
587             }
588         }
589         if( nEndPos > nStartPos )
590         {
591             if( nStartPos == 0 )
592                 rExport.GetDocHandler()->characters( rText );
593             else
594                 rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nEndPos - nStartPos)) );
595         }
596     }
597     else // do not convert tabs and linefeeds (eg for numbers coming from unit converter)
598     {
599         rExport.GetDocHandler()->characters( rText );
600     }
601 }
602 
603 void exportRangeToSomewhere( SvXMLExport& rExport, const ::rtl::OUString& rValue )
604 {
605     //with issue #i366# and CWS chart20 ranges for error bars were introduced
606     //to keep them during copy paste from calc to impress for example it
607     //was necessary to introduce a mapping between the used ranges within calc and the data written to the local table
608     //this is why we write this ranges here
609 
610     //#i113950# first the range was exported to attribute text:id, but that attribute does not allow arbitrary strings anymore within ODF 1.2
611     //as an alternative the range info is now saved into the description at an empty group element (not very nice, but ODF conform)
612 
613     const SvtSaveOptions::ODFDefaultVersion nCurrentODFVersion( SvtSaveOptions().GetODFDefaultVersion() );
614     if( nCurrentODFVersion == SvtSaveOptions::ODFVER_010 || nCurrentODFVersion == SvtSaveOptions::ODFVER_011 )
615         return;//svg:desc is not allowed at draw:g in ODF1.0; but as the ranges for error bars are anyhow not allowed within ODF1.0 nor ODF1.1 we do not need the information
616 
617     SvXMLElementExport aEmptyShapeGroup( rExport, XML_NAMESPACE_DRAW,
618                               ::xmloff::token::GetXMLToken( ::xmloff::token::XML_G ),
619                               sal_True, sal_False );
620     SvXMLElementExport aDescription( rExport, XML_NAMESPACE_SVG,
621                               ::xmloff::token::GetXMLToken( ::xmloff::token::XML_DESC ),
622                               sal_True, sal_False );
623     rExport.GetDocHandler()->characters( rValue );
624 }
625 
626 Reference< chart2::XRegressionCurve > getRegressionCurve(
627     const Reference< chart2::XDataSeries > & xDataSeries )
628 {
629     Reference< chart2::XRegressionCurve > xResult;
630 
631     Reference< chart2::XRegressionCurveContainer > xRegCurveCnt( xDataSeries, uno::UNO_QUERY );
632     if( xRegCurveCnt.is())
633     {
634         // find equation properties of first regression curve
635         Sequence< Reference< chart2::XRegressionCurve > > aCurveSeq(
636             xRegCurveCnt->getRegressionCurves() );
637         for( sal_Int32 nI=0; nI<aCurveSeq.getLength(); ++nI )
638         {
639             // skip mean-value line
640             Reference< lang::XServiceName > xServiceName( aCurveSeq[nI], uno::UNO_QUERY );
641             if( xServiceName.is())
642             {
643                 OUString aServiceName( xServiceName->getServiceName());
644                 if( aServiceName.equalsAsciiL(
645                         RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart2.MeanValueRegressionCurve" )))
646                     continue;
647             }
648             // take first non-empty curve
649             if( aCurveSeq[nI].is())
650             {
651                 xResult.set( aCurveSeq[nI] );
652                 break;
653             }
654         }
655     }
656     return xResult;
657 }
658 
659 void setXMLRangePropertyAtDataSequence(
660     const Reference< chart2::data::XDataSequence > & xDataSequence,
661     const OUString & rXMLRange )
662 {
663     if( !xDataSequence.is())
664         return;
665     try
666     {
667         const OUString aXMLRangePropName( RTL_CONSTASCII_USTRINGPARAM( "CachedXMLRange" ));
668         Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW );
669         Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo());
670         if( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName ))
671             xProp->setPropertyValue( aXMLRangePropName, uno::makeAny( rXMLRange ));
672     }
673     catch( const uno::Exception & ex )
674     {
675         (void)ex; // avoid warning for pro build
676         OSL_ENSURE( false, ::rtl::OUStringToOString(
677                         ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Exception caught, Message: " )) +
678                         ex.Message, RTL_TEXTENCODING_ASCII_US ).getStr());
679     }
680 }
681 
682 bool getXMLRangePropertyFromDataSequence(
683     const Reference< chart2::data::XDataSequence > & xDataSequence,
684     OUString & rOutXMLRange,
685     bool bClearProp /* = false */)
686 {
687     bool bResult = false;
688     if( xDataSequence.is())
689     {
690         try
691         {
692             const OUString aXMLRangePropName( RTL_CONSTASCII_USTRINGPARAM( "CachedXMLRange" ));
693             Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW );
694             Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo());
695             bResult =
696                 ( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName ) &&
697                   ( xProp->getPropertyValue( aXMLRangePropName ) >>= rOutXMLRange ) &&
698                   rOutXMLRange.getLength());
699             // clear the property after usage
700             if( bClearProp && bResult )
701                 xProp->setPropertyValue( aXMLRangePropName, uno::Any( OUString()));
702         }
703         catch( const uno::Exception & ex )
704         {
705             (void)ex; // avoid warning for pro build
706             OSL_ENSURE( false, ::rtl::OUStringToOString(
707                             ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Exception caught, Message: " )) +
708                             ex.Message, RTL_TEXTENCODING_ASCII_US ).getStr());
709         }
710     }
711     return bResult;
712 }
713 
714 void copyProperties(
715     const Reference< beans::XPropertySet > & xSource,
716     const Reference< beans::XPropertySet > & xDestination )
717 {
718     if( ! (xSource.is() && xDestination.is()) )
719         return;
720 
721     try
722     {
723         Reference< beans::XPropertySetInfo > xSrcInfo( xSource->getPropertySetInfo(), uno::UNO_QUERY_THROW );
724         Reference< beans::XPropertySetInfo > xDestInfo( xDestination->getPropertySetInfo(), uno::UNO_QUERY_THROW );
725         Sequence< beans::Property > aProperties( xSrcInfo->getProperties());
726         const sal_Int32 nLength = aProperties.getLength();
727         for( sal_Int32 i = 0; i < nLength; ++i )
728         {
729             OUString aName( aProperties[i].Name);
730             if( xDestInfo->hasPropertyByName( aName ))
731             {
732                 beans::Property aProp( xDestInfo->getPropertyByName( aName ));
733                 if( (aProp.Attributes & beans::PropertyAttribute::READONLY) == 0 )
734                     xDestination->setPropertyValue(
735                         aName, xSource->getPropertyValue( aName ));
736             }
737         }
738     }
739     catch( const uno::Exception & )
740     {
741         OSL_ENSURE( false, "Copying property sets failed!" );
742     }
743 }
744 
745 bool switchBackToDataProviderFromParent( const Reference< chart2::XChartDocument >& xChartDoc, const tSchXMLLSequencesPerIndex & rLSequencesPerIndex )
746 {
747     //return whether the switch is successful
748     if( !xChartDoc.is() || !xChartDoc->hasInternalDataProvider() )
749         return false;
750     Reference< chart2::data::XDataProvider > xDataProviderFromParent( SchXMLTools::getDataProviderFromParent( xChartDoc ) );
751     if( !xDataProviderFromParent.is() )
752         return false;
753     uno::Reference< chart2::data::XDataReceiver > xDataReceiver( xChartDoc, uno::UNO_QUERY );
754     if( !xDataReceiver.is() )
755         return false;
756 
757     xDataReceiver->attachDataProvider( xDataProviderFromParent );
758 
759     for( tSchXMLLSequencesPerIndex::const_iterator aLSeqIt( rLSequencesPerIndex.begin() );
760          aLSeqIt != rLSequencesPerIndex.end(); ++aLSeqIt )
761     {
762         Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( aLSeqIt->second );
763         if( !xLabeledSeq.is() )
764             continue;
765         Reference< chart2::data::XDataSequence > xNewSeq;
766         xNewSeq = lcl_createNewSequenceFromCachedXMLRange( xLabeledSeq->getValues(), xDataProviderFromParent );
767         if( xNewSeq.is() )
768             xLabeledSeq->setValues( xNewSeq );
769         xNewSeq = lcl_createNewSequenceFromCachedXMLRange( xLabeledSeq->getLabel(), xDataProviderFromParent );
770         if( xNewSeq.is() )
771             xLabeledSeq->setLabel( xNewSeq );
772     }
773     return true;
774 }
775 
776 void setBuildIDAtImportInfo( uno::Reference< frame::XModel > xModel, Reference< beans::XPropertySet > xImportInfo )
777 {
778     ::rtl::OUString aGenerator( lcl_getGeneratorFromModelOrItsParent(xModel) );
779     if( aGenerator.getLength() )
780         SvXMLMetaDocumentContext::setBuildId( aGenerator, xImportInfo );
781 }
782 
783 bool isDocumentGeneratedWithOpenOfficeOlderThan3_3( const uno::Reference< frame::XModel >& xChartModel )
784 {
785     bool bResult = isDocumentGeneratedWithOpenOfficeOlderThan3_0( xChartModel );
786     if( !bResult )
787     {
788         ::rtl::OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
789         if( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org_project/3") ) ) != -1 )
790         {
791             if( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org_project/300m") ) ) != -1 )
792             {
793                 sal_Int32 nBuilId = lcl_getBuildIDFromGenerator( lcl_getGeneratorFromModel(xChartModel) );
794                 if( nBuilId>0 && nBuilId<9491 ) //9491 is build id of dev300m76
795                     bResult= true;
796             }
797             else if( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org_project/310m") ) ) != -1 )
798                 bResult= true;
799             else if( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org_project/320m") ) ) != -1 )
800                 bResult= true;
801         }
802     }
803     return bResult;
804 }
805 
806 bool isDocumentGeneratedWithOpenOfficeOlderThan3_0( const uno::Reference< frame::XModel >& xChartModel )
807 {
808     bool bResult = isDocumentGeneratedWithOpenOfficeOlderThan2_3( xChartModel );
809     if( !bResult )
810     {
811         ::rtl::OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
812         if( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org_project/680m") ) ) != -1 )
813             bResult= true;
814     }
815     return bResult;
816 }
817 
818 bool isDocumentGeneratedWithOpenOfficeOlderThan2_4( const uno::Reference< frame::XModel >& xChartModel )
819 {
820     if( isDocumentGeneratedWithOpenOfficeOlderThan2_3( xChartModel ) )
821         return true;
822 
823     if( isDocumentGeneratedWithOpenOfficeOlderThan3_0( xChartModel ) )
824     {
825         sal_Int32 nBuilId = lcl_getBuildIDFromGenerator( lcl_getGeneratorFromModel(xChartModel) );
826         if( nBuilId>0 && nBuilId<=9238 ) //9238 is build id of OpenOffice.org 2.3.1
827             return true;
828     }
829     return false;
830 }
831 
832 bool isDocumentGeneratedWithOpenOfficeOlderThan2_3( const uno::Reference< frame::XModel >& xChartModel )
833 {
834     bool bResult = false;
835     ::rtl::OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
836     //if there is a meta stream at the chart object it was not written with an older OpenOffice version < 2.3
837     if( !aGenerator.getLength() )
838     {
839         //if there is no meta stream at the chart object we need to check whether the parent document is OpenOffice at all
840         uno::Reference< container::XChild > xChild( xChartModel, uno::UNO_QUERY );
841         if( xChild.is() )
842         {
843             aGenerator = lcl_getGeneratorFromModel( uno::Reference< frame::XModel >( xChild->getParent(), uno::UNO_QUERY) );
844             if( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org_project") ) ) != -1 )
845             {
846                 //the chart application has not created files without a meta stream since OOo 2.3 (OOo 2.3 has written a metastream already)
847                 //only the report builder extension has created some files with OOo 3.1 that do not have a meta stream
848                 if( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org_project/31") ) ) != -1 )
849                     bResult = false;//#i100102# probably generated with OOo 3.1 by the report designer
850                 else
851                     bResult= true; //in this case the OLE chart was created by an older version, as OLE objects are sometimes stream copied the version can differ from the parents version, so the parents version is not a reliable indicator
852             }
853             else if( isDocumentGeneratedWithOpenOfficeOlderThan2_0(xChartModel) )
854                 bResult= true;
855         }
856     }
857     return bResult;
858 }
859 
860 bool isDocumentGeneratedWithOpenOfficeOlderThan2_0( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XModel >& xChartModel)
861 {
862     bool bResult = false;
863     ::rtl::OUString aGenerator( lcl_getGeneratorFromModelOrItsParent(xChartModel) );
864     if(    ( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("OpenOffice.org 1") ) ) == 0 )
865         || ( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("StarOffice 6") ) ) == 0 )
866         || ( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("StarOffice 7") ) ) == 0 )
867         || ( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("StarSuite 6") ) ) == 0 )
868         || ( aGenerator.indexOf( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("StarSuite 7") ) ) == 0 )
869         )
870         bResult= true;
871     return bResult;
872 }
873 
874 Reference< chart2::data::XDataProvider > getDataProviderFromParent( const Reference< chart2::XChartDocument >& xChartDoc )
875 {
876     Reference< chart2::data::XDataProvider > xRet;
877     uno::Reference< container::XChild > xChild( xChartDoc, uno::UNO_QUERY );
878     if( xChild.is() )
879     {
880         Reference< lang::XMultiServiceFactory > xFact( xChild->getParent(), uno::UNO_QUERY );
881         if( xFact.is() )
882         {
883             const OUString aDataProviderServiceName( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.chart2.data.DataProvider"));
884             const uno::Sequence< OUString > aServiceNames( xFact->getAvailableServiceNames());
885             const OUString * pBegin = aServiceNames.getConstArray();
886             const OUString * pEnd = pBegin + aServiceNames.getLength();
887             if( ::std::find( pBegin, pEnd, aDataProviderServiceName ) != pEnd )
888             {
889                 xRet = Reference< chart2::data::XDataProvider >(
890                     xFact->createInstance( aDataProviderServiceName ), uno::UNO_QUERY );
891             }
892         }
893     }
894     return xRet;
895 }
896 
897 } // namespace SchXMLTools
898