/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <xmlprmap.hxx>

#include "SchXMLExport.hxx"
#include "XMLChartPropertySetMapper.hxx"

#include <tools/debug.hxx>
#include <rtl/logfile.hxx>

#include <tools/globname.hxx>
#include <sot/clsids.hxx>

#include "xmluconv.hxx"
#include <rtl/math.hxx>
// header for any2enum
#include <comphelper/extract.hxx>

#include <list>

// when Issue #i75865# is fixed the following line must be uncommented
// #define ISSUE_75865_IS_FIXED

#include <com/sun/star/chart/ChartLegendPosition.hpp>
#include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
#include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
#include <com/sun/star/chart/XAxisZSupplier.hpp>
#include <com/sun/star/chart/XChartDataArray.hpp>
#include <com/sun/star/chart/ChartAxisAssign.hpp>
#include <com/sun/star/chart/X3DDisplay.hpp>
#include <com/sun/star/chart/XStatisticDisplay.hpp>

#include <com/sun/star/drawing/XDrawPageSupplier.hpp>

#include    "MultiPropertySetHandler.hxx"
#include    "PropertyMap.hxx"
namespace binfilter {

using namespace ::com::sun::star;
using namespace ::binfilter::xmloff::token;
using namespace ::com::sun::star::uno;

using rtl::OUString;
using rtl::OUStringBuffer;

using ::rtl::OUString;

struct SchXMLDataPointStruct
{
    ::rtl::OUString maStyleName;
    sal_Int32       mnRepeat;

    SchXMLDataPointStruct() : mnRepeat( 1 ) {}
};

// ========================================
// class SchXMLExportHelper
// ========================================

SchXMLExportHelper::SchXMLExportHelper(
    SvXMLExport& rExport,
    SvXMLAutoStylePoolP& rASPool ) :
        mrExport( rExport ),
        mrAutoStylePool( rASPool ),
        mnDomainAxes( 0 ),
        mnSeriesCount( 0 ),
        mnSeriesLength( 0 ),
        mbHasSeriesLabels( sal_False ),
        mbHasCategoryLabels( sal_False ),
        mbRowSourceColumns( sal_True )
        // #110680#
        // this id depends on the ServiceManager used due to the binary filter stripping.
        // ,msCLSID( ::rtl::OUString( SvGlobalName( SO3_SCH_CLASSID ).GetHexName()))
{
    // #110680#
    // changed initialisation for msCLSID. Compare the ServiceInfo name with
    // the known name of the LegacyServiceManager.
    Reference<lang::XServiceInfo> xServiceInfo( mrExport.getServiceFactory(), UNO_QUERY );
    DBG_ASSERT( xServiceInfo.is(), "XMultiServiceFactory without xServiceInfo (!)" );
    OUString rdbURL = xServiceInfo->getImplementationName();
    OUString implLegacyServiceManagerName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.office.LegacyServiceManager" ) );

    if( rdbURL.equals( implLegacyServiceManagerName ))
    {
        msCLSID = ::rtl::OUString( SvGlobalName( BF_SO3_SCH_CLASSID ).GetHexName());
    }
    else
    {
        msCLSID = ::rtl::OUString( SvGlobalName( SO3_SCH_CLASSID ).GetHexName());
    }

    msTableName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "local-table" ));

    // create factory
    mxPropertyHandlerFactory = new XMLChartPropHdlFactory;

    if( mxPropertyHandlerFactory.is() )
    {
        // create property set mapper
        mxPropertySetMapper = new XMLChartPropertySetMapper;

    }

    mxExpPropMapper = new XMLChartExportPropertyMapper( mxPropertySetMapper, rExport );

    // register chart auto-style family
    mrAutoStylePool.AddFamily(
        XML_STYLE_FAMILY_SCH_CHART_ID,
        ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( XML_STYLE_FAMILY_SCH_CHART_NAME )),
        mxExpPropMapper.get(),
        ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( XML_STYLE_FAMILY_SCH_CHART_PREFIX )));

    // register shape family
    mrAutoStylePool.AddFamily(
        XML_STYLE_FAMILY_SD_GRAPHICS_ID,
        ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( XML_STYLE_FAMILY_SD_GRAPHICS_NAME )),
        mxExpPropMapper.get(),
        ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( XML_STYLE_FAMILY_SD_GRAPHICS_PREFIX )));
    // register paragraph family also for shapes
    mrAutoStylePool.AddFamily(
        XML_STYLE_FAMILY_TEXT_PARAGRAPH,
        GetXMLToken( XML_PARAGRAPH ),
        mxExpPropMapper.get(),
        ::rtl::OUString( sal_Unicode( 'P' )));
    // register text family also for shapes
    mrAutoStylePool.AddFamily(
        XML_STYLE_FAMILY_TEXT_TEXT,
        GetXMLToken( XML_TEXT ),
        mxExpPropMapper.get(),
        ::rtl::OUString( sal_Unicode( 'T' )));
}

const ::rtl::OUString& SchXMLExportHelper::getChartCLSID()
{
    return msCLSID;
}

void SchXMLExportHelper::exportAutoStyles()
{
    if( mxExpPropMapper.is())
    {
        //ToDo: when embedded in calc/writer this is not necessary because the
        // numberformatter is shared between both documents
        mrExport.exportAutoDataStyles();

        // export chart auto styles
        mrAutoStylePool.exportXML(
            XML_STYLE_FAMILY_SCH_CHART_ID
            , mrExport.GetDocHandler(),
            mrExport.GetMM100UnitConverter(),
            mrExport.GetNamespaceMap()
            );

        // export auto styles for additional shapes
        mrExport.GetShapeExport()->exportAutoStyles();
        // and for text in additional shapes
        mrExport.GetTextParagraphExport()->exportTextAutoStyles();
    }
}

void SchXMLExportHelper::collectAutoStyles( uno::Reference< chart::XChartDocument > rChartDoc )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogContext, "xmloff", "bm93744", "::SchXMLExportHelper::collectAutoStyles" );

    parseDocument( rChartDoc, sal_False );
}

void SchXMLExportHelper::exportChart( uno::Reference< chart::XChartDocument > rChartDoc,
                                      sal_Bool bIncludeTable )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogContext, "xmloff", "bm93744", "::SchXMLExportHelper::exportChart" );

    parseDocument( rChartDoc, sal_True, bIncludeTable );
    DBG_ASSERT( maAutoStyleNameQueue.empty(), "There are still remaining autostyle names in the queue" );
}


// private methods
// ---------------

/// if bExportContent is false the auto-styles are collected
void SchXMLExportHelper::parseDocument( uno::Reference< chart::XChartDocument >& rChartDoc,
                                        sal_Bool bExportContent,
                                        sal_Bool bIncludeTable )
{
    uno::Reference< chart::XDiagram > xDiagram = rChartDoc->getDiagram();

    uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY );
    ::rtl::OUString sChartType ( xDiagram->getDiagramType());
    if( xDiaProp.is())
    {
        try
        {
            chart::ChartDataRowSource eRowSource;
            uno::Any aAny =
                xDiaProp->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DataRowSource" )));

            cppu::any2enum< chart::ChartDataRowSource >( eRowSource, aAny );
            mbRowSourceColumns = ( eRowSource == chart::ChartDataRowSource_COLUMNS );

            // the chart core treats donut chart with interchanged rows/columns
            if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.DonutDiagram" )))
            {
                mbRowSourceColumns = ! mbRowSourceColumns;
            }
        }
        catch( uno::Exception )
        {
            OSL_FAIL( "exportChart: Error getting Property \"DataRowSource\"" );
        }
    }

    if( ! rChartDoc.is())
    {
        OSL_FAIL( "No XChartDocument was given for export." );
        return;
    }

    // get Properties of ChartDocument
    sal_Bool bHasMainTitle = sal_False;
    sal_Bool bHasSubTitle = sal_False;
    sal_Bool bHasLegend = sal_False;

    ::std::vector< XMLPropertyState > aPropertyStates;

    uno::Reference< beans::XPropertySet > xDocPropSet( rChartDoc, uno::UNO_QUERY );
    if( xDocPropSet.is())
    {
        try
        {
            uno::Any aAny( xDocPropSet->getPropertyValue(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HasMainTitle" ))));
            aAny >>= bHasMainTitle;
            aAny = xDocPropSet->getPropertyValue(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HasSubTitle" )));
            aAny >>= bHasSubTitle;
            aAny = xDocPropSet->getPropertyValue(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HasLegend" )));
            aAny >>= bHasLegend;
        }
        catch( beans::UnknownPropertyException )
        {
            DBG_WARNING( "Required property not found in ChartDocument" );
        }
    }

    // get some values of general interest

    // reset
    mbHasSeriesLabels = mbHasCategoryLabels = sal_False;
    mnSeriesCount = mnSeriesLength = 0;

    // set
    uno::Reference< chart::XChartDataArray > xData( rChartDoc->getData(), uno::UNO_QUERY );
    if( xData.is())
    {
        uno::Sequence< uno::Sequence< double > > xValues = xData->getData();

        if( xValues.getLength())
        {
            // determine size of data
            const uno::Sequence< double >* pSequence = xValues.getConstArray();
            uno::Sequence< ::rtl::OUString > xSeriesLabels;
            uno::Sequence< ::rtl::OUString > xCategoryLabels;

            if( mbRowSourceColumns )
            {
                mnSeriesCount = pSequence->getLength();
                mnSeriesLength = xValues.getLength();
                xSeriesLabels = xData->getColumnDescriptions();
                xCategoryLabels = xData->getRowDescriptions();
            }
            else
            {
                mnSeriesCount = xValues.getLength();
                mnSeriesLength = pSequence->getLength();
                xSeriesLabels = xData->getRowDescriptions();
                xCategoryLabels = xData->getColumnDescriptions();
            }

            // determine existence of headers
            mbHasCategoryLabels = ( xCategoryLabels.getLength() > 0 );
            mbHasSeriesLabels = ( xSeriesLabels.getLength() > 0 );
        }
    }

    // chart element
    // -------------

    SvXMLElementExport* pElChart = 0;
    // get property states for autostyles
    if( mxExpPropMapper.is())
    {
        uno::Reference< beans::XPropertySet > xPropSet( rChartDoc->getArea(), uno::UNO_QUERY );
        if( xPropSet.is())
            aPropertyStates = mxExpPropMapper->Filter( xPropSet );
    }

    // attributes
    // determine class
    // domain axes are also needed for auto-styles
    if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.XYDiagram" )))
        mnDomainAxes = 1;
    else
        mnDomainAxes = 0;

    if( bExportContent )
    {
        // attributes
        // determine class
        if( sChartType.getLength())
        {
            enum XMLTokenEnum eXMLChartType = XML_TOKEN_INVALID;
            ::rtl::OUString sAddInName;

            if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.LineDiagram" )))
                eXMLChartType = XML_LINE;
            else if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.AreaDiagram" )))
                eXMLChartType = XML_AREA;
            else if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.BarDiagram" )))
                eXMLChartType = XML_BAR;
            else if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.PieDiagram" )))
                eXMLChartType = XML_CIRCLE;
            else if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.DonutDiagram" )))
                eXMLChartType = XML_RING;
            else if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.XYDiagram" )))
            {
                eXMLChartType = XML_SCATTER;
                mnDomainAxes = 1;
            }
            else if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.NetDiagram" )))
                eXMLChartType = XML_RADAR;
            else if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.StockDiagram" )))
                eXMLChartType = XML_STOCK;
            else    // servie-name of add-in
            {
                eXMLChartType = XML_ADD_IN;
                sAddInName = sChartType;
            }

            if( eXMLChartType != XML_TOKEN_INVALID )
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, eXMLChartType );

            if( sAddInName.getLength())
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ADD_IN_NAME, sAddInName );

            // translated rows/columns
            if( xDocPropSet.is())
            {
                sal_Bool bTranslate = sal_False;

                ::rtl::OUString aTransPropName= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TranslatedColumns" ));;
                enum XMLTokenEnum eTransToken = ::binfilter::xmloff::token::XML_COLUMN_MAPPING;
                uno::Any aAny = xDocPropSet->getPropertyValue(
                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HasTranslatedColumns" )));

                aAny >>= bTranslate;

                if( ! bTranslate )
                {
                    aAny = xDocPropSet->getPropertyValue(
                        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HasTranslatedRows" )));
                    aAny >>= bTranslate;
                    if( bTranslate )
                    {
                        aTransPropName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TranslatedRows" ));
                        eTransToken = ::binfilter::xmloff::token::XML_ROW_MAPPING;
                    }
                }

                if( bTranslate )
                {
                    uno::Sequence< sal_Int32 > aSeq;
                    aAny = xDocPropSet->getPropertyValue( aTransPropName );
                    if( aAny >>= aSeq )
                    {
                        const sal_Int32* pArray = aSeq.getConstArray();
                        const sal_Int32 nSize = aSeq.getLength();
                        sal_Int32 i = 0;
                        ::rtl::OUStringBuffer aBuf;
                        for( i = 0; i < nSize; ++i )
                        {
                            aBuf.append( pArray[ i ], 10 );
                            if( i != (nSize - 1))
                                aBuf.append( static_cast< sal_Unicode >( ' ' ));
                        }

                        mrExport.AddAttribute( XML_NAMESPACE_CHART,
                                               ::binfilter::xmloff::token::GetXMLToken( eTransToken ),
                                               aBuf.makeStringAndClear() );
                    }
                }
            }
        }
        // write style name
        AddAutoStyleAttribute( aPropertyStates );

        //element
        pElChart = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_CHART, sal_True, sal_True );
    }
    else    // autostyles
    {
        CollectAutoStyle( aPropertyStates );
    }
    // remove property states for autostyles
    aPropertyStates.clear();

    // title element
    // -------------

    if( bHasMainTitle )
    {
        // get property states for autostyles
        if( mxExpPropMapper.is())
        {
            uno::Reference< beans::XPropertySet > xPropSet( rChartDoc->getTitle(), uno::UNO_QUERY );
            if( xPropSet.is())
                aPropertyStates = mxExpPropMapper->Filter( xPropSet );
        }
        if( bExportContent )
        {
            uno::Reference< drawing::XShape > xShape = rChartDoc->getTitle();
            if( xShape.is())    // && "hasTitleBeenMoved"
                addPosition( xShape );

            // write style name
            AddAutoStyleAttribute( aPropertyStates );

            // element
            SvXMLElementExport aElTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, sal_True, sal_True );

            // content (text:p)
            uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
            if( xPropSet.is())
            {
                uno::Any aAny( xPropSet->getPropertyValue(
                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "String" ))));
                ::rtl::OUString aText;
                aAny >>= aText;
                exportText( aText );
            }
        }
        else    // autostyles
        {
            CollectAutoStyle( aPropertyStates );
        }
        // remove property states for autostyles
        aPropertyStates.clear();
    }

    // subtitle element
    // ----------------

    if( bHasSubTitle )
    {
        // get property states for autostyles
        if( mxExpPropMapper.is())
        {
            uno::Reference< beans::XPropertySet > xPropSet( rChartDoc->getSubTitle(), uno::UNO_QUERY );
            if( xPropSet.is())
                aPropertyStates = mxExpPropMapper->Filter( xPropSet );
        }

        if( bExportContent )
        {
            uno::Reference< drawing::XShape > xShape = rChartDoc->getSubTitle();
            if( xShape.is())
                addPosition( xShape );

            // write style name
            AddAutoStyleAttribute( aPropertyStates );

            // element (has no subelements)
            SvXMLElementExport aElSubTitle( mrExport, XML_NAMESPACE_CHART, XML_SUBTITLE, sal_True, sal_True );

            // content (text:p)
            uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
            if( xPropSet.is())
            {
                uno::Any aAny( xPropSet->getPropertyValue(
                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "String" ))));
                ::rtl::OUString aText;
                aAny >>= aText;
                exportText( aText );
            }
        }
        else    // autostyles
        {
            CollectAutoStyle( aPropertyStates );
        }
        // remove property states for autostyles
        aPropertyStates.clear();
    }

    // legend element
    // --------------
    if( bHasLegend )
    {
        // get property states for autostyles
        if( mxExpPropMapper.is())
        {
            uno::Reference< beans::XPropertySet > xPropSet( rChartDoc->getLegend(), uno::UNO_QUERY );
            if( xPropSet.is())
                aPropertyStates = mxExpPropMapper->Filter( xPropSet );
        }

        if( bExportContent )
        {
            uno::Reference< beans::XPropertySet > xProp( rChartDoc->getLegend(), uno::UNO_QUERY );
            if( xProp.is())
            {
                chart::ChartLegendPosition aLegendPos = chart::ChartLegendPosition_NONE;
                try
                {
                    uno::Any aAny( xProp->getPropertyValue(
                        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Alignment" ))));
                    aAny >>= aLegendPos;
                }
                catch( beans::UnknownPropertyException )
                {
                    DBG_WARNING( "Property Align not found in ChartLegend" );
                }

                switch( aLegendPos )
                {
                    case chart::ChartLegendPosition_LEFT:
                        msString = GetXMLToken(XML_LEFT);
                        break;
                    case chart::ChartLegendPosition_RIGHT:
                        msString = GetXMLToken(XML_RIGHT);
                        break;
                    case chart::ChartLegendPosition_TOP:
                        msString = GetXMLToken(XML_TOP);
                        break;
                    case chart::ChartLegendPosition_BOTTOM:
                        msString = GetXMLToken(XML_BOTTOM);
                        break;
                    default:
                        break;
                }

                // export anchor position
                if( msString.getLength())
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LEGEND_POSITION, msString );

                // export absolute position
                msString = ::rtl::OUString();
                uno::Reference< drawing::XShape > xShape( xProp, uno::UNO_QUERY );
                if( xShape.is())
                    addPosition( xShape );
            }

            // write style name
            AddAutoStyleAttribute( aPropertyStates );

            // element
            SvXMLElementExport aLegend( mrExport, XML_NAMESPACE_CHART, XML_LEGEND, sal_True, sal_True );
        }
        else    // autostyles
        {
            CollectAutoStyle( aPropertyStates );
        }
        // remove property states for autostyles
        aPropertyStates.clear();
    }

    // plot-area element
    // -----------------
    if( xDiagram.is())
        exportPlotArea( xDiagram, bExportContent, bIncludeTable );

    // export additional shapes
    // ------------------------
    if( xDocPropSet.is() )
    {
        if( bExportContent )
        {
            if( mxAdditionalShapes.is())
            {
                // can't call exportShapes with all shapes because the
                // initialisation happend with the complete draw page and not
                // the XShapes object used here. Thus the shapes have to be
                // exported one by one
                UniReference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport();
                uno::Reference< drawing::XShape > xShape;
                const sal_Int32 nShapeCount( mxAdditionalShapes->getCount());
                for( sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++ )
                {
                    mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape;
                    DBG_ASSERT( xShape.is(), "Shape without an XShape?" );
                    if( ! xShape.is())
                        continue;

                    rShapeExport->exportShape( xShape );
                }
                // this would be the easier way if it worked:
                //mrExport.GetShapeExport()->exportShapes( mxAdditionalShapes );
            }
        }
        else
        {
            // get a sequence of non-chart shapes (inserted via clipboard)
            uno::Any aShapesAny = xDocPropSet->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AdditionalShapes" )));
            aShapesAny >>= mxAdditionalShapes;

            if( mxAdditionalShapes.is())
            {
                // seek shapes has to be called for the whole page because in
                // the shape export the vector of shapes is accessed via the
                // ZOrder which might be (actually is) larger than the number of
                // shapes in mxAdditionalShapes
                uno::Reference< drawing::XDrawPageSupplier > xSupplier( rChartDoc, uno::UNO_QUERY );
                DBG_ASSERT( xSupplier.is(), "Cannot retrieve draw page to initialize shape export" );
                if( xSupplier.is() )
                {
                    uno::Reference< drawing::XShapes > xDrawPage( xSupplier->getDrawPage(), uno::UNO_QUERY );
                    DBG_ASSERT( xDrawPage.is(), "Invalid draw page for initializing shape export" );
                    if( xDrawPage.is())
                        mrExport.GetShapeExport()->seekShapes( xDrawPage );
                }

                // can't call collectShapesAutoStyles with all shapes because
                // the initialisation happend with the complete draw page and
                // not the XShapes object used here. Thus the shapes have to be
                // exported one by one
                UniReference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport();
                uno::Reference< drawing::XShape > xShape;
                const sal_Int32 nShapeCount( mxAdditionalShapes->getCount());
                for( sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++ )
                {
                    mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape;
                    DBG_ASSERT( xShape.is(), "Shape without an XShape?" );
                    if( ! xShape.is())
                        continue;

                    rShapeExport->collectShapeAutoStyles( xShape );
                }
                // this would be the easier way if it worked:
                // mrExport.GetShapeExport()->collectShapesAutoStyles( mxAdditionalShapes );
            }
        }
    }

    // table element
    // (is included as subelement of chart)
    // ------------------------------------
    if( bExportContent )
    {
        // #85929# always export table, otherwise clipboard may loose data
        exportTable( xData );
    }

    // close <chart:chart> element
    if( pElChart )
        delete pElChart;
}

void SchXMLExportHelper::exportTable( uno::Reference< chart::XChartDataArray >& rData )
{
    // table element
    // -------------
    mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, msTableName );
    SvXMLElementExport aTable( mrExport, XML_NAMESPACE_TABLE, XML_TABLE, sal_True, sal_True );

    if( rData.is())
    {
        double fData;

        // get NaN
        double fSolarNaN;
        ::rtl::math::setNan( &fSolarNaN );
        double fNaN = fSolarNaN;
        sal_Bool bConvertNaN = sal_False;
        uno::Reference< chart::XChartData > xChartData( rData, uno::UNO_QUERY );
        if( xChartData.is())
        {
            fNaN = xChartData->getNotANumber();
            bConvertNaN = ( ! ::rtl::math::isNan( fNaN ));
        }

        uno::Sequence< uno::Sequence< double > > xValues = rData->getData();
        if( xValues.getLength())
        {
            const uno::Sequence< double >* pSequence = xValues.getConstArray();
            const double* pData = 0;

            sal_Int32 nSeries, nDataPoint;

            // export column headers
            uno::Sequence< ::rtl::OUString > xSeriesLabels = rData->getColumnDescriptions();
            uno::Sequence< ::rtl::OUString > xCategoryLabels = rData->getRowDescriptions();
            sal_Int32 nSeriesCount, nSeriesLength;
            if( mbRowSourceColumns )
            {
                nSeriesLength = mnSeriesLength;
                nSeriesCount = mnSeriesCount;
            }
            else
            {
                nSeriesLength = mnSeriesCount;
                nSeriesCount = mnSeriesLength;
            }
            sal_Int32 nSeriesLablesLength = xSeriesLabels.getLength();
            sal_Int32 nCategoryLabelsLength = xCategoryLabels.getLength();

            // columns
            if( mbHasCategoryLabels )
            {
                // row description are put in the first column
                SvXMLElementExport aHeaderColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, sal_True, sal_True );
                SvXMLElementExport aHeaderColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, sal_True, sal_True );
            }
            // non-header columns
            if( mnSeriesCount )
            {
                SvXMLElementExport aColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMNS, sal_True, sal_True );
                mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
                                       ::rtl::OUString::valueOf( (sal_Int64) mnSeriesCount ));
                SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, sal_True, sal_True );
            }

            // rows
            if( mbHasSeriesLabels )
            {
                SvXMLElementExport aHeaderRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, sal_True, sal_True );
                SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, sal_True, sal_True );
                // write one empty cell (the cell A1 is never used)
                {
                    SvXMLElementExport aEmptyCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, sal_True, sal_True );
                    SvXMLElementExport aEmptyParagraph( mrExport, XML_NAMESPACE_TEXT, XML_P, sal_True, sal_True );
                }
                for( nSeries = 0; nSeries < nSeriesLablesLength; nSeries++ )
                {
                    mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VALUE_TYPE, XML_STRING );
                    SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, sal_True, sal_True );
                    exportText( xSeriesLabels[ nSeries ] );
                }
            }

            // export data
            SvXMLElementExport aRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROWS, sal_True, sal_True );
            for( nDataPoint = 0; nDataPoint < nSeriesLength; nDataPoint++ )
            {
                // <table:table-row>
                SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, sal_True, sal_True );
                pData = pSequence[ nDataPoint ].getConstArray();

                if( mbHasCategoryLabels )
                {
                    // cells containing row descriptions (in the first column)
                    mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VALUE_TYPE, XML_STRING );
                    SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, sal_True, sal_True );
                    if( nDataPoint < nCategoryLabelsLength )
                        exportText( xCategoryLabels[ nDataPoint ] );
                }

                for( nSeries = 0; nSeries < nSeriesCount; nSeries++ )
                {
                    // get string by value
                    fData = pData[ nSeries ];

                    // convert NaN
                    if( bConvertNaN &&  // implies xChartData.is()
                        xChartData->isNotANumber( fData ))
                        fData = fSolarNaN;

                    SvXMLUnitConverter::convertDouble( msStringBuffer, fData );
                    msString = msStringBuffer.makeStringAndClear();

                    // <table:table-cell>
                    mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VALUE_TYPE, XML_FLOAT );
                    mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VALUE, msString );
                    SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, sal_True, sal_True );

                    // <text:p>
                    exportText( msString, false ); // do not convert tabs and lfs
                }
            }
        }
    }
}

void SchXMLExportHelper::exportPlotArea( uno::Reference< chart::XDiagram > xDiagram,
                                         sal_Bool bExportContent,
                                         sal_Bool bIncludeTable )
{
    DBG_ASSERT( xDiagram.is(), "Invalid XDiagram as parameter" );
    if( ! xDiagram.is())
        return;

    // variables for autostyles
    uno::Reference< beans::XPropertySet > xPropSet;
    std::vector< XMLPropertyState > aPropertyStates;
    ::rtl::OUString aASName;
    sal_Bool bHasTwoYAxes = sal_False;
    sal_Bool bIs3DChart = sal_False;
    drawing::HomogenMatrix aTransMatrix;

    msStringBuffer.setLength( 0 );

    // plot-area element
    // -----------------

    SvXMLElementExport* pElPlotArea = 0;
    // get property states for autostyles
    xPropSet = uno::Reference< beans::XPropertySet >( xDiagram, uno::UNO_QUERY );
    if( xPropSet.is())
    {
        if( mxExpPropMapper.is())
            aPropertyStates = mxExpPropMapper->Filter( xPropSet );
    }
    if( bExportContent )
    {
        UniReference< XMLShapeExport > rShapeExport;

        // write style name
        AddAutoStyleAttribute( aPropertyStates );

        if( msChartAddress.getLength())
        {
            mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, msChartAddress );

            uno::Reference< chart::XChartDocument > xDoc( mrExport.GetModel(), uno::UNO_QUERY );
            if( xDoc.is() )
            {
                uno::Reference< beans::XPropertySet > xDocProp( xDoc, uno::UNO_QUERY );
                if( xDocProp.is())
                {
                    uno::Any aAny;
                    sal_Bool bFirstCol(sal_False), bFirstRow(sal_False);

                    try
                    {
                        aAny = xDocProp->getPropertyValue(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DataSourceLabelsInFirstColumn" )));
                        aAny >>= bFirstCol;
                        aAny = xDocProp->getPropertyValue(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DataSourceLabelsInFirstRow" )));
                        aAny >>= bFirstRow;

                        if( bFirstCol || bFirstRow )
                        {
                            mrExport.AddAttribute( XML_NAMESPACE_CHART,
                                                   ::binfilter::xmloff::token::GetXMLToken( ::binfilter::xmloff::token::XML_DATA_SOURCE_HAS_LABELS ),
                                                   ( bFirstCol
                                                     ? ( bFirstRow
                                                         ?  ::binfilter::xmloff::token::GetXMLToken( ::binfilter::xmloff::token::XML_BOTH )
                                                         :  ::binfilter::xmloff::token::GetXMLToken( ::binfilter::xmloff::token::XML_COLUMN ))
                                                     : ::binfilter::xmloff::token::GetXMLToken( ::binfilter::xmloff::token::XML_ROW )));
                        }
                    }
                    catch( beans::UnknownPropertyException )
                    {
                        DBG_ERRORFILE( "Properties missing" );
                    }
                }
            }
        }

        if( msTableNumberList.getLength())
        {
            // this attribute is for charts embedded in calc documents only.
            // With this you are able to store a file again in 5.0 binary format
            mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_TABLE_NUMBER_LIST, msTableNumberList );
        }

        // attributes
        uno::Reference< drawing::XShape > xShape ( xDiagram, uno::UNO_QUERY );
        if( xShape.is())
        {
            addPosition( xShape );
            addSize( xShape );
        }

        if( xPropSet.is())
        {
            uno::Any aAny;
            try
            {
                aAny = xPropSet->getPropertyValue(
                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HasSecondaryYAxis" )));
                aAny >>= bHasTwoYAxes;
            }
            catch( beans::UnknownPropertyException )
            {
                OSL_FAIL( "Property HasSecondaryYAxis not found in Diagram" );
            }

            // 3d attributes
            try
            {
                aAny = xPropSet->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Dim3D" )));
                aAny >>= bIs3DChart;

                if( bIs3DChart )
                {
                    rShapeExport = mrExport.GetShapeExport();
                    if( rShapeExport.is())
                        rShapeExport->export3DSceneAttributes( xPropSet );
                }
            }
            catch (const uno::Exception &rEx)
            {
#if OSL_DEBUG_LEVEL > 1
                rtl::OString aBStr(rtl::OUStringToOString(rEx.Message, RTL_TEXTENCODING_ASCII_US));
                OSL_TRACE( "chart:exportPlotAreaException caught: %s", aBStr.getStr());
#else
                (void)rEx;
#endif
            }
        }

        // element
        pElPlotArea = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_PLOT_AREA, sal_True, sal_True );

        // light sources (inside plot area element)
        if( bIs3DChart &&
            rShapeExport.is())
            rShapeExport->export3DLamps( xPropSet );
    }
    else    // autostyles
    {
        CollectAutoStyle( aPropertyStates );
    }
    // remove property states for autostyles
    aPropertyStates.clear();

    // axis elements
    // -------------
    exportAxes( xDiagram, bExportContent );

    // categories element
    // ------------------
    sal_Int32 nDataPointOffset = mbHasSeriesLabels? 1 : 0;
    sal_Int32 nSeriesOffset = mbHasCategoryLabels ? 1 : 0;

    if( bExportContent &&
        mbHasCategoryLabels )
    {
        // fill msString with cell-range-address of categories
        if( bIncludeTable )
        {
            // export own table references
            msStringBuffer.append( msTableName );

            getCellAddress( 0, nDataPointOffset );
            msStringBuffer.append( (sal_Unicode) ':' );
            getCellAddress( 0, mnSeriesLength + nDataPointOffset - 1 );
        }
        else
        {
            // deprecated
//              msString = msCategoriesAddress;
        }

        if( msStringBuffer.getLength())
        {
            msString = msStringBuffer.makeStringAndClear();
            mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, msString );
            SvXMLElementExport aCategories( mrExport, XML_NAMESPACE_CHART, XML_CATEGORIES, sal_True, sal_True );
        }
    }

    // series elements
    // ---------------
    msStringBuffer.setLength( 0 );
    SvXMLElementExport* pSeries = NULL;
    ::rtl::OUString aSeriesASName;
    sal_Int32 nAttachedAxis;

    sal_Int32 nNumberOfLinesInBarChart = 0;
    sal_Bool bStockHasVolume = sal_False;
    if( bExportContent )
    {
        if( 0 == xDiagram->getDiagramType().reverseCompareToAsciiL(
                RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.BarDiagram" )))
        {
            try
            {
                uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY_THROW );
                xDiaProp->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NumberOfLines" )))
                    >>= nNumberOfLinesInBarChart;
            }
            catch(const uno::Exception & aEx)
            {
                rtl::OString aBStr(rtl::OUStringToOString(aEx.Message, RTL_TEXTENCODING_ASCII_US));
                OSL_TRACE( "Exception caught for property NumberOfLines: %s", aBStr.getStr());
            }
        }
        else if( 0 == xDiagram->getDiagramType().reverseCompareToAsciiL(
                RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.StockDiagram" )))
        {
            try
            {
                uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY_THROW );
                xDiaProp->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Volume" )))
                    >>= bStockHasVolume;
            }
            catch (const uno::Exception & aEx)
            {
                rtl::OString aBStr(rtl::OUStringToOString(aEx.Message, RTL_TEXTENCODING_ASCII_US));
                OSL_TRACE( "Exception caught for property Volume: %s", aBStr.getStr());
            }
        }
    }

    uno::Sequence< uno::Sequence< sal_Int32 > > aDataPointSeq;
    if( xPropSet.is())
    {
        uno::Any aAny = xPropSet->getPropertyValue(
            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AttributedDataPoints" )));
        aAny >>= aDataPointSeq;
        DBG_ASSERT( mnSeriesCount == aDataPointSeq.getLength(), "DataPointSequence has wrong size" );
    }

    for( sal_Int32 nSeries = mnDomainAxes; nSeries < mnSeriesCount; nSeries++ )
    {
        nAttachedAxis = chart::ChartAxisAssign::PRIMARY_Y;
        sal_Bool bHasMeanValueLine = false;
        chart::ChartRegressionCurveType eRegressionType( chart::ChartRegressionCurveType_NONE );
        chart::ChartErrorIndicatorType eErrorType( chart::ChartErrorIndicatorType_NONE );

        // get property states for autostyles
        xPropSet = xDiagram->getDataRowProperties( nSeries );
        if( xPropSet.is())
        {
            // determine attached axis
            try
            {
                uno::Any aAny( xPropSet->getPropertyValue(
                                   OUString( RTL_CONSTASCII_USTRINGPARAM( "Axis" ))));
                aAny >>= nAttachedAxis;

                aAny = xPropSet->getPropertyValue(
                    OUString( RTL_CONSTASCII_USTRINGPARAM ( "MeanValue" )));
                aAny >>= bHasMeanValueLine;

                aAny = xPropSet->getPropertyValue(
                    OUString( RTL_CONSTASCII_USTRINGPARAM( "RegressionCurves" )));
                aAny >>= eRegressionType;

                aAny = xPropSet->getPropertyValue(
                    OUString( RTL_CONSTASCII_USTRINGPARAM( "ErrorIndicator" )));
                aAny >>= eErrorType;
            }
            catch( beans::UnknownPropertyException aEx )
            {
                OSL_TRACE(
                    OUStringToOString(
                        OUString( RTL_CONSTASCII_USTRINGPARAM(
                                      "Required property not found in DataRowProperties: " )) +
                        aEx.Message,
                        RTL_TEXTENCODING_ASCII_US ).getStr());
            }

            if( mxExpPropMapper.is())
            {
                aPropertyStates = mxExpPropMapper->Filter( xPropSet );
            }
        }

        if( bExportContent )
        {
            if( bIncludeTable )
            {
                // export own table references
                msStringBuffer.append( msTableName );

                getCellAddress( nSeries + nDataPointOffset, nSeriesOffset );
                msStringBuffer.append( (sal_Unicode) ':' );
                getCellAddress( nSeries + nDataPointOffset, nSeriesOffset + mnSeriesLength - 1 );

                msString = msStringBuffer.makeStringAndClear();
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, msString );

                // reference to label
                if( mbHasSeriesLabels )
                {
                    msStringBuffer.append( msTableName );
                    getCellAddress( nSeries + nDataPointOffset, 0 );
                    msString = msStringBuffer.makeStringAndClear();
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, msString );
                }
            }
            else
            {
                // #81525# convert addresses for xy charts
                // this should be done by calc in the future

                // deprecated
//                  if( maSeriesAddresses.getLength() > nSeries )
//                  {
//                      mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS,
//                                             maSeriesAddresses[ nSeries ].DataRangeAddress );
//                      if( maSeriesAddresses[ nSeries ].LabelAddress.getLength())
//                          mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS,
//                                                 maSeriesAddresses[ nSeries ].LabelAddress );
//                  }
            }

            if( bHasTwoYAxes )
            {
                if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
                else
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
            }

            // write style name
//            AddAutoStyleAttribute( aPropertyStates );   // can't be used here because we need the name
            if( !aPropertyStates.empty())
            {
                DBG_ASSERT( ! maAutoStyleNameQueue.empty(), "Autostyle queue empty!" );
                if( maAutoStyleNameQueue.empty())
                {
                    aSeriesASName = OUString();
                }
                else
                {
                    aSeriesASName = maAutoStyleNameQueue.front();
                    maAutoStyleNameQueue.pop();
                }
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aSeriesASName );
            }

            // chart-type for mixed types
            if( nNumberOfLinesInBarChart > 0 &&
                nSeries >= (mnSeriesCount - mnDomainAxes - nNumberOfLinesInBarChart))
            {
#ifdef ISSUE_75865_IS_FIXED
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_LINE );
#else
                OUString aNameSpacedToken( RTL_CONSTASCII_USTRINGPARAM( "chart:" ));
                aNameSpacedToken += xmloff::token::GetXMLToken( XML_LINE );
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, aNameSpacedToken );
#endif
            }

            // #i32366# first series gets type "bar" for stock with volume charts
            if( bStockHasVolume && (nSeries - mnDomainAxes) == 0 )
            {
#ifdef ISSUE_75865_IS_FIXED
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_BAR );
#else
                OUString aNameSpacedToken( RTL_CONSTASCII_USTRINGPARAM( "chart:" ));
                aNameSpacedToken += xmloff::token::GetXMLToken( XML_BAR );
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, aNameSpacedToken );
#endif
            }

            // open series element until end of for loop
            pSeries = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_SERIES, sal_True, sal_True );
        }
        else    // autostyles
        {
            CollectAutoStyle( aPropertyStates );
        }
        // remove property states for autostyles
        aPropertyStates.clear();

        // domain element if necessary
        // ---------------------------
        if( bExportContent &&
            mnDomainAxes > 0 &&
            nSeries ==  mnDomainAxes )
        {
            msStringBuffer.setLength( 0 );
            for( sal_Int32 nDomain = 0; nDomain < mnDomainAxes; nDomain++ )
            {
                if( bIncludeTable )
                {
                    // first series has a domain, that is the first table row (column)
                    msStringBuffer.append( msTableName );

                    getCellAddress( nDomain + nSeriesOffset, nDataPointOffset );
                    msStringBuffer.append( (sal_Unicode) ':' );
                    getCellAddress( nDomain + nSeriesOffset, nDataPointOffset + mnSeriesLength - 1 );
                }
                else
                {
                    // #81525# convert addresses for xy charts
                    // this should be done by calc in the future

                    // deprecated
//                      if( maSeriesAddresses.getLength() > 0 )
//                      {
//                          msStringBuffer.append( maSeriesAddresses[ 0 ].DataRangeAddress );
//                      }
                }

                msString = msStringBuffer.makeStringAndClear();
                if( msString.getLength())
                {
                    mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, msString );
                }
                SvXMLElementExport aDomain( mrExport, XML_NAMESPACE_CHART, XML_DOMAIN, sal_True, sal_True );
            }
        }

        // statistical objects:
        // regression curves and mean value lines
        if( bHasMeanValueLine &&
            xPropSet.is() &&
            mxExpPropMapper.is() )
        {
            uno::Reference< beans::XPropertySet > xStatProp;
            uno::Any aPropAny( xPropSet->getPropertyValue(
                                   OUString( RTL_CONSTASCII_USTRINGPARAM( "DataMeanValueProperties" ))));
            aPropAny >>= xStatProp;

            if( xStatProp.is() )
            {
                aPropertyStates = mxExpPropMapper->Filter( xStatProp );

                if( !aPropertyStates.empty() )
                {
                    // write element
                    if( bExportContent )
                    {
                        // add style name attribute
                        AddAutoStyleAttribute( aPropertyStates );

                        SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_MEAN_VALUE, sal_True, sal_True );
                    }
                    else    // autostyles
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                }
            }
        }

        if( eRegressionType != chart::ChartRegressionCurveType_NONE &&
            xPropSet.is() &&
            mxExpPropMapper.is() )
        {
            uno::Reference< beans::XPropertySet > xStatProp;
            uno::Any aPropAny( xPropSet->getPropertyValue(
                                   OUString( RTL_CONSTASCII_USTRINGPARAM( "DataRegressionProperties" ))));
            aPropAny >>= xStatProp;

            if( xStatProp.is() )
            {
                aPropertyStates = mxExpPropMapper->Filter( xStatProp );

                if( !aPropertyStates.empty() )
                {
                    // write element
                    if( bExportContent )
                    {
                        // add style name attribute
                        AddAutoStyleAttribute( aPropertyStates );
                        SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_REGRESSION_CURVE, sal_True, sal_True );
                    }
                    else    // autostyles
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                }
            }
        }

        if( eErrorType != chart::ChartErrorIndicatorType_NONE &&
            xPropSet.is() &&
            mxExpPropMapper.is() )
        {
            uno::Reference< beans::XPropertySet > xStatProp;
            uno::Any aPropAny( xPropSet->getPropertyValue(
                                   OUString( RTL_CONSTASCII_USTRINGPARAM( "DataErrorProperties" ))));
            aPropAny >>= xStatProp;

            if( xStatProp.is() )
            {
                aPropertyStates = mxExpPropMapper->Filter( xStatProp );

                if( !aPropertyStates.empty() )
                {
                    // write element
                    if( bExportContent )
                    {
                        // add style name attribute
                        AddAutoStyleAttribute( aPropertyStates );
                        SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_ERROR_INDICATOR, sal_True, sal_True );
                    }
                    else    // autostyles
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                }
            }
        }

        // data-points
        // -----------
        // write data-points only if they contain autostyles
        // objects with equal autostyles are grouped using the attribute
        // repeat="number"

        // Note: if only the nth data-point has autostyles there is an element
        // without style and repeat="n-1" attribute written in advance.

        // the sequence aDataPointSeq contains indices of data-points that
        // do have own attributes.  This increases the performance substantially.

        // more performant version for #93600#
        if( mxExpPropMapper.is())
        {
            uno::Sequence< sal_Int32 > aPtSeq = aDataPointSeq[ nSeries ];
            sal_Int32 nSize = aPtSeq.getLength();
            DBG_ASSERT( nSize <= mnSeriesLength, "Too many point attributes" );

            const sal_Int32 * pPoints = aPtSeq.getConstArray();
            sal_Int32 nElement;
            sal_Int32 nRepeat;

            if( bExportContent )
            {
                ::std::list< SchXMLDataPointStruct > aDataPointList;

                sal_Int32 nLastIndex = -1;
                sal_Int32 nCurrIndex;

                // collect elements
                for( nElement = 0; nElement < nSize; ++nElement )
                {
                    aPropertyStates.clear();
                    nCurrIndex = pPoints[ nElement ];

                    // write leading empty data points
                    if( nCurrIndex - nLastIndex > 1 )
                    {
                        SchXMLDataPointStruct aPoint;
                        aPoint.mnRepeat = nCurrIndex - nLastIndex - 1;
                        aDataPointList.push_back( aPoint );
                    }

                    // get property states
                    try
                    {
                        xPropSet = xDiagram->getDataPointProperties( nCurrIndex, nSeries );
                    }
                    catch( uno::Exception aEx )
                    {
                        OSL_TRACE( "Exception caught during Export of data point: %s",
                                    OUStringToOString( aEx.Message, RTL_TEXTENCODING_ASCII_US ).getStr() );
                    }
                    if( xPropSet.is())
                    {
                        aPropertyStates = mxExpPropMapper->Filter( xPropSet );
                        if( !aPropertyStates.empty() )
                        {
                            // write data-point with style
                            DBG_ASSERT( ! maAutoStyleNameQueue.empty(), "Autostyle queue empty!" );

                            SchXMLDataPointStruct aPoint;
                            if( ! maAutoStyleNameQueue.empty())
                            {
                                aPoint.maStyleName = maAutoStyleNameQueue.front();
                                maAutoStyleNameQueue.pop();
                            }

                            aDataPointList.push_back( aPoint );
                            nLastIndex = nCurrIndex;
                            continue;
                        }
                    }

                    // if we get here the property states are empty
                    SchXMLDataPointStruct aPoint;
                    aDataPointList.push_back( aPoint );

                    nLastIndex = nCurrIndex;
                }
                // final empty elements
                nRepeat = mnSeriesLength - nLastIndex - 1;
                if( nRepeat > 0 )
                {
                    SchXMLDataPointStruct aPoint;
                    aPoint.mnRepeat = nRepeat;
                    aDataPointList.push_back( aPoint );
                }

                // write elements (merge equal ones)
                ::std::list< SchXMLDataPointStruct >::iterator aIter = aDataPointList.begin();
                SchXMLDataPointStruct aPoint;
                SchXMLDataPointStruct aLastPoint;

                // initialize so that it doesn't matter if
                // the element is counted in the first iteration
                aLastPoint.mnRepeat = 0;

                for( ; aIter != aDataPointList.end(); ++aIter )
                {
                    aPoint = (*aIter);

                    if( aPoint.maStyleName == aLastPoint.maStyleName )
                        aPoint.mnRepeat += aLastPoint.mnRepeat;
                    else if( aLastPoint.mnRepeat > 0 )
                    {
                        // write last element
                        if( aLastPoint.maStyleName.getLength() )
                            mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );

                        if( aLastPoint.mnRepeat > 1 )
                            mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
                                                   ::rtl::OUString::valueOf( (sal_Int64)( aLastPoint.mnRepeat ) ));

                        SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, sal_True, sal_True );
                    }
                    aLastPoint = aPoint;
                }
                // write last element if it hasn't been written in last iteration
                if( aPoint.maStyleName == aLastPoint.maStyleName )
                {
                    if( aLastPoint.maStyleName.getLength() )
                        mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );

                    if( aLastPoint.mnRepeat > 1 )
                        mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
                                               ::rtl::OUString::valueOf( (sal_Int64)( aLastPoint.mnRepeat ) ));

                    SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, sal_True, sal_True );
                }
            }
            else
            {
                // collect autostyles
                for( nElement = 0; nElement < nSize; ++nElement )
                {
                    try
                    {
                        xPropSet = xDiagram->getDataPointProperties( pPoints[ nElement ], nSeries );
                    }
                    catch( uno::Exception aEx )
                    {
                        OSL_TRACE( "Exception caught during Export of data point: %s",
                                    OUStringToOString( aEx.Message, RTL_TEXTENCODING_ASCII_US ).getStr() );
                    }
                    if( xPropSet.is())
                    {
                        aPropertyStates = mxExpPropMapper->Filter( xPropSet );
                        if( !aPropertyStates.empty() )
                            CollectAutoStyle( aPropertyStates );
                    }
                }
            }
        }

        // close series element
        if( pSeries )
            delete pSeries;
    }

    // stock-chart elements
    ::rtl::OUString sChartType ( xDiagram->getDiagramType());
    if( 0 == sChartType.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart.StockDiagram" )))
    {
        uno::Reference< chart::XStatisticDisplay > xStockPropProvider( xDiagram, uno::UNO_QUERY );
        if( xStockPropProvider.is())
        {
            // stock-gain-marker
            uno::Reference< beans::XPropertySet > xStockPropSet = xStockPropProvider->getUpBar();
            if( xStockPropSet.is())
            {
                aPropertyStates.clear();
                aPropertyStates = mxExpPropMapper->Filter( xStockPropSet );

                if( !aPropertyStates.empty() )
                {
                    if( bExportContent )
                    {
                        AddAutoStyleAttribute( aPropertyStates );

                        SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_GAIN_MARKER, sal_True, sal_True );
                    }
                    else
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                }
            }

            // stock-loss-marker
            xStockPropSet = xStockPropProvider->getDownBar();
            if( xStockPropSet.is())
            {
                aPropertyStates.clear();
                aPropertyStates = mxExpPropMapper->Filter( xStockPropSet );

                if( !aPropertyStates.empty() )
                {
                    if( bExportContent )
                    {
                        AddAutoStyleAttribute( aPropertyStates );

                        SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_LOSS_MARKER, sal_True, sal_True );
                    }
                    else
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                }
            }

            // stock-range-line
            xStockPropSet = xStockPropProvider->getMinMaxLine();
            if( xStockPropSet.is())
            {
                aPropertyStates.clear();
                aPropertyStates = mxExpPropMapper->Filter( xStockPropSet );

                if( !aPropertyStates.empty() )
                {
                    if( bExportContent )
                    {
                        AddAutoStyleAttribute( aPropertyStates );

                        SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_RANGE_LINE, sal_True, sal_True );
                    }
                    else
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                }
            }
        }
    }

    // wall and floor element
    // ----------------------

    uno::Reference< chart::X3DDisplay > xWallFloorSupplier( xDiagram, uno::UNO_QUERY );
    if( mxExpPropMapper.is() &&
        xWallFloorSupplier.is())
    {
        // remove property states for autostyles
        aPropertyStates.clear();

        uno::Reference< beans::XPropertySet > xWallPropSet( xWallFloorSupplier->getWall(), uno::UNO_QUERY );
        if( xWallPropSet.is())
        {
            aPropertyStates = mxExpPropMapper->Filter( xWallPropSet );

            if( aPropertyStates.empty() )
            {
                // write element
                if( bExportContent )
                {
                    // add style name attribute
                    AddAutoStyleAttribute( aPropertyStates );

                    SvXMLElementExport aWall( mrExport, XML_NAMESPACE_CHART, XML_WALL, sal_True, sal_True );
                }
                else    // autostyles
                {
                    CollectAutoStyle( aPropertyStates );
                }
            }
        }

        // floor element
        // -------------

        // remove property states for autostyles
        aPropertyStates.clear();

        uno::Reference< beans::XPropertySet > xFloorPropSet( xWallFloorSupplier->getFloor(), uno::UNO_QUERY );
        if( xFloorPropSet.is())
        {
            aPropertyStates = mxExpPropMapper->Filter( xFloorPropSet );

            if( aPropertyStates.empty() )
            {
                // write element
                if( bExportContent )
                {
                    // add style name attribute
                    AddAutoStyleAttribute( aPropertyStates );

                    SvXMLElementExport aFloor( mrExport, XML_NAMESPACE_CHART, XML_FLOOR, sal_True, sal_True );
                }
                else    // autostyles
                {
                    CollectAutoStyle( aPropertyStates );
                }
            }
        }
    }

    if( pElPlotArea )
        delete pElPlotArea;
}

void SchXMLExportHelper::exportAxes( uno::Reference< chart::XDiagram > xDiagram, sal_Bool bExportContent )
{
    DBG_ASSERT( xDiagram.is(), "Invalid XDiagram as parameter" );
    if( ! xDiagram.is())
        return;

    // variables for autostyles
    const ::rtl::OUString sNumFormat( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NumberFormat" )));
    sal_Int32 nNumberFormat(0);
    uno::Reference< beans::XPropertySet > xPropSet;
    std::vector< XMLPropertyState > aPropertyStates;
    ::rtl::OUString aASName;

    // get some properties from document first
    sal_Bool bHasXAxis = sal_False,
        bHasYAxis = sal_False,
        bHasZAxis = sal_False,
        bHasSecondaryXAxis = sal_False,
        bHasSecondaryYAxis = sal_False;
    sal_Bool bHasXAxisTitle = sal_False,
        bHasYAxisTitle = sal_False,
        bHasZAxisTitle = sal_False;
    sal_Bool bHasXAxisMajorGrid = sal_False,
        bHasXAxisMinorGrid = sal_False,
        bHasYAxisMajorGrid = sal_False,
        bHasYAxisMinorGrid = sal_False,
        bHasZAxisMajorGrid = sal_False,
        bHasZAxisMinorGrid = sal_False;
    sal_Bool bIs3DChart = sal_False;

    // get multiple properties using XMultiPropertySet
    MultiPropertySetHandler aDiagramProperties (xDiagram);

    //  Check for supported services and then the properties provided by this service.
    Reference<lang::XServiceInfo> xServiceInfo (xDiagram, UNO_QUERY);
    if (xServiceInfo.is())
    {
        if (xServiceInfo->supportsService(
            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart.ChartAxisXSupplier" ))))
        {
            aDiagramProperties.Add (
                OUString(RTL_CONSTASCII_USTRINGPARAM("HasXAxis")), bHasXAxis);
        }
        if (xServiceInfo->supportsService(
            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart.ChartAxisYSupplier" ))))
        {
            aDiagramProperties.Add (
                OUString(RTL_CONSTASCII_USTRINGPARAM("HasYAxis")), bHasYAxis);
        }
        if (xServiceInfo->supportsService(
            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart.ChartAxisZSupplier" ))))
        {
            aDiagramProperties.Add (
                OUString(RTL_CONSTASCII_USTRINGPARAM("HasZAxis")), bHasZAxis);
        }
        if (xServiceInfo->supportsService(
            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart.ChartTwoAxisXSupplier" ))))
        {
            aDiagramProperties.Add (
                OUString(RTL_CONSTASCII_USTRINGPARAM("HasSecondaryXAxis")), bHasSecondaryXAxis);
        }
        if (xServiceInfo->supportsService(
            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart.ChartTwoAxisYSupplier" ))))
        {
            aDiagramProperties.Add (
                OUString(RTL_CONSTASCII_USTRINGPARAM("HasSecondaryYAxis")), bHasSecondaryYAxis);
        }
    }

    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasXAxisTitle")), bHasXAxisTitle);
    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasYAxisTitle")), bHasYAxisTitle);
    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasZAxisTitle")), bHasZAxisTitle);

    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasXAxisGrid")), bHasXAxisMajorGrid);
    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasYAxisGrid")), bHasYAxisMajorGrid);
    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasZAxisGrid")), bHasZAxisMajorGrid);

    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasXAxisHelpGrid")), bHasXAxisMinorGrid);
    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasYAxisHelpGrid")), bHasYAxisMinorGrid);
    aDiagramProperties.Add (
        OUString (RTL_CONSTASCII_USTRINGPARAM ("HasZAxisHelpGrid")), bHasZAxisMinorGrid);

    aDiagramProperties.Add(
        OUString (RTL_CONSTASCII_USTRINGPARAM ("Dim3D")), bIs3DChart);

    if ( ! aDiagramProperties.GetProperties ())
    {
        DBG_WARNING ("Required properties not found in Chart diagram");
    }

    SvXMLElementExport* pAxis = NULL;

    // x axis
    // -------

    // write axis element also if the axis itself is not visible, but a grid or
    // title
    if( bHasXAxis ||
        bHasXAxisTitle || bHasXAxisMajorGrid || bHasXAxisMinorGrid )
    {
        uno::Reference< chart::XAxisXSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
        if( xAxisSupp.is())
        {
            // get property states for autostyles
            if( mxExpPropMapper.is())
            {
                xPropSet = xAxisSupp->getXAxis();
                if( xPropSet.is())
                {
                    uno::Any aNumAny = xPropSet->getPropertyValue( sNumFormat );
                    aNumAny >>= nNumberFormat;
                    if( nNumberFormat != -1 )
                        mrExport.addDataStyle( nNumberFormat );

                    aPropertyStates = mxExpPropMapper->Filter( xPropSet );
                }
            }
            if( bExportContent )
            {
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, (mnDomainAxes > 0)
                                       ? XML_DOMAIN      // scatter (or bubble) chart
                                       : XML_CATEGORY );

                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, XML_PRIMARY_X );


                // write style name
                AddAutoStyleAttribute( aPropertyStates );

                // element
                pAxis = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, sal_True, sal_True );
            }
            else    // autostyles
            {
                CollectAutoStyle( aPropertyStates );
            }
            aPropertyStates.clear();

            // axis-title
            if( bHasXAxisTitle )
            {
                uno::Reference< beans::XPropertySet > xTitleProp( xAxisSupp->getXAxisTitle(), uno::UNO_QUERY );
                if( xTitleProp.is())
                {
                    aPropertyStates = mxExpPropMapper->Filter( xTitleProp );
                    if( bExportContent )
                    {
                        ::rtl::OUString aText;
                        uno::Any aAny( xTitleProp->getPropertyValue(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "String" ))));
                        aAny >>= aText;

                        uno::Reference< drawing::XShape > xShape( xTitleProp, uno::UNO_QUERY );
                        if( xShape.is())
                            addPosition( xShape );

                        AddAutoStyleAttribute( aPropertyStates );
                        SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, sal_True, sal_True );

                        // paragraph containing title
                        exportText( aText );
                    }
                    else
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                    aPropertyStates.clear();
                }
            }

            // grid
            uno::Reference< beans::XPropertySet > xMajorGrid( xAxisSupp->getXMainGrid(), uno::UNO_QUERY );
            if( bHasXAxisMajorGrid && xMajorGrid.is())
            {
                aPropertyStates = mxExpPropMapper->Filter( xMajorGrid );
                if( bExportContent )
                {
                    AddAutoStyleAttribute( aPropertyStates );
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_MAJOR );
                    SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, sal_True, sal_True );
                }
                else
                {
                    CollectAutoStyle( aPropertyStates );
                }
                aPropertyStates.clear();
            }
            uno::Reference< beans::XPropertySet > xMinorGrid( xAxisSupp->getXHelpGrid(), uno::UNO_QUERY );
            if( bHasXAxisMinorGrid && xMinorGrid.is())
            {
                aPropertyStates = mxExpPropMapper->Filter( xMinorGrid );
                if( bExportContent )
                {
                    AddAutoStyleAttribute( aPropertyStates );
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_MINOR );
                    SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, sal_True, sal_True );
                }
                else
                {
                    CollectAutoStyle( aPropertyStates );
                }
                aPropertyStates.clear();
            }
            if( pAxis )
            {
                delete pAxis;
                pAxis = NULL;
            }
        }
    }

    // secondary x axis
    if( bHasSecondaryXAxis )
    {
        uno::Reference< chart::XTwoAxisXSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
        if( xAxisSupp.is())
        {
            // get property states for autostyles
            if( mxExpPropMapper.is())
            {
                xPropSet = xAxisSupp->getSecondaryXAxis();
                if( xPropSet.is())
                {
                    uno::Any aNumAny = xPropSet->getPropertyValue( sNumFormat );
                    aNumAny >>= nNumberFormat;
                    if( nNumberFormat != -1 )
                        mrExport.addDataStyle( nNumberFormat );

                    aPropertyStates = mxExpPropMapper->Filter( xPropSet );
                }
            }
            if( bExportContent )
            {
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, (mnDomainAxes > 0)
                                       ? XML_DOMAIN      // scatter (or bubble) chart
                                       : XML_CATEGORY );

                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, XML_SECONDARY_X );
                AddAutoStyleAttribute( aPropertyStates );
                pAxis = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, sal_True, sal_True );
            }
            else    // autostyles
            {
                CollectAutoStyle( aPropertyStates );
            }
            aPropertyStates.clear();

            if( pAxis )
            {
                delete pAxis;
                pAxis = NULL;
            }
        }
    }

    // y axis
    // -------

    // write axis element also if the axis itself is not visible, but a grid or
    // title
    if( bHasYAxis ||
        bHasYAxisTitle || bHasYAxisMajorGrid || bHasYAxisMinorGrid )
    {
        uno::Reference< chart::XAxisYSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
        if( xAxisSupp.is())
        {
            // get property states for autostyles
            if( mxExpPropMapper.is())
            {
                xPropSet = xAxisSupp->getYAxis();
                if( xPropSet.is())
                {
                    uno::Any aNumAny = xPropSet->getPropertyValue( sNumFormat );
                    aNumAny >>= nNumberFormat;
                    if( nNumberFormat != -1 )
                        mrExport.addDataStyle( nNumberFormat );

                    aPropertyStates = mxExpPropMapper->Filter( xPropSet );
                }
            }
            if( bExportContent )
            {
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_VALUE );
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, XML_PRIMARY_Y );
                AddAutoStyleAttribute( aPropertyStates );
                pAxis = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, sal_True, sal_True );
            }
            else
            {
                CollectAutoStyle( aPropertyStates );
            }
            aPropertyStates.clear();

            // axis-title
            if( bHasYAxisTitle )
            {
                uno::Reference< beans::XPropertySet > xTitleProp( xAxisSupp->getYAxisTitle(), uno::UNO_QUERY );
                if( xTitleProp.is())
                {
                    aPropertyStates = mxExpPropMapper->Filter( xTitleProp );
                    if( bExportContent )
                    {
                        ::rtl::OUString aText;
                        uno::Any aAny( xTitleProp->getPropertyValue(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "String" ))));
                        aAny >>= aText;

                        uno::Reference< drawing::XShape > xShape( xTitleProp, uno::UNO_QUERY );
                        if( xShape.is())
                            addPosition( xShape );

                        AddAutoStyleAttribute( aPropertyStates );
                        SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, sal_True, sal_True );

                        // paragraph containing title
                        exportText( aText );
                    }
                    else
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                    aPropertyStates.clear();
                }
            }

            // grid
            uno::Reference< beans::XPropertySet > xMajorGrid( xAxisSupp->getYMainGrid(), uno::UNO_QUERY );
            if( bHasYAxisMajorGrid && xMajorGrid.is())
            {
                aPropertyStates = mxExpPropMapper->Filter( xMajorGrid );

                if( bExportContent )
                {
                    AddAutoStyleAttribute( aPropertyStates );
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_MAJOR );
                    SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, sal_True, sal_True );
                }
                else
                {
                    CollectAutoStyle( aPropertyStates );
                }
                aPropertyStates.clear();
            }
            // minor grid
            uno::Reference< beans::XPropertySet > xMinorGrid( xAxisSupp->getYHelpGrid(), uno::UNO_QUERY );
            if( bHasYAxisMinorGrid && xMinorGrid.is())
            {
                aPropertyStates = mxExpPropMapper->Filter( xMinorGrid );

                if( bExportContent )
                {
                    AddAutoStyleAttribute( aPropertyStates );
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_MINOR );
                    SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, sal_True, sal_True );
                }
                else
                {
                    CollectAutoStyle( aPropertyStates );
                }
                aPropertyStates.clear();
            }
            if( pAxis )
            {
                delete pAxis;
                pAxis = NULL;
            }
        }
    }
    if( bHasSecondaryYAxis )
    {
        uno::Reference< chart::XTwoAxisYSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
        if( xAxisSupp.is())
        {
            // get property states for autostyles
            if( mxExpPropMapper.is())
            {
                xPropSet = xAxisSupp->getSecondaryYAxis();
                if( xPropSet.is())
                {
                    uno::Any aNumAny = xPropSet->getPropertyValue( sNumFormat );
                    aNumAny >>= nNumberFormat;
                    if( nNumberFormat != -1 )
                        mrExport.addDataStyle( nNumberFormat );

                    aPropertyStates = mxExpPropMapper->Filter( xPropSet );
                }
            }
            if( bExportContent )
            {
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_VALUE );
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, XML_SECONDARY_Y );
                AddAutoStyleAttribute( aPropertyStates );
                pAxis = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, sal_True, sal_True );
            }
            else    // autostyles
            {
                CollectAutoStyle( aPropertyStates );
            }
            aPropertyStates.clear();
            if( pAxis )
            {
                delete pAxis;
                pAxis = NULL;
            }
        }
    }

    // z axis
    // -------

    if( bHasZAxis &&
        bIs3DChart )
    {
        uno::Reference< chart::XAxisZSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
        if( xAxisSupp.is())
        {
            // get property states for autostyles
            if( mxExpPropMapper.is())
            {
                xPropSet = xAxisSupp->getZAxis();
                if( xPropSet.is())
                {
                    uno::Any aNumAny = xPropSet->getPropertyValue( sNumFormat );
                    aNumAny >>= nNumberFormat;
                    if( nNumberFormat != -1 )
                        mrExport.addDataStyle( nNumberFormat );

                    aPropertyStates = mxExpPropMapper->Filter( xPropSet );
                }
            }
            if( bExportContent )
            {
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_SERIES );
                mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, XML_PRIMARY_Z );

                AddAutoStyleAttribute( aPropertyStates );
                pAxis = new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, sal_True, sal_True );
            }
            else
            {
                CollectAutoStyle( aPropertyStates );
            }
            aPropertyStates.clear();

            // axis-title
            if( bHasZAxisTitle )
            {
                uno::Reference< beans::XPropertySet > xTitleProp( xAxisSupp->getZAxisTitle(), uno::UNO_QUERY );
                if( xTitleProp.is())
                {
                    aPropertyStates = mxExpPropMapper->Filter( xTitleProp );
                    if( bExportContent )
                    {
                        ::rtl::OUString aText;
                        uno::Any aAny( xTitleProp->getPropertyValue(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "String" ))));
                        aAny >>= aText;

                        uno::Reference< drawing::XShape > xShape( xTitleProp, uno::UNO_QUERY );
                        if( xShape.is())
                            addPosition( xShape );

                        AddAutoStyleAttribute( aPropertyStates );
                        SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, sal_True, sal_True );

                        // paragraph containing title
                        exportText( aText );
                    }
                    else
                    {
                        CollectAutoStyle( aPropertyStates );
                    }
                    aPropertyStates.clear();
                }
            }

            // grid
            uno::Reference< beans::XPropertySet > xMajorGrid( xAxisSupp->getZMainGrid(), uno::UNO_QUERY );
            if( bHasZAxisMajorGrid && xMajorGrid.is())
            {
                aPropertyStates = mxExpPropMapper->Filter( xMajorGrid );

                if( bExportContent )
                {
                    AddAutoStyleAttribute( aPropertyStates );
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_MAJOR );
                    SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, sal_True, sal_True );
                }
                else
                {
                    CollectAutoStyle( aPropertyStates );
                }
                aPropertyStates.clear();
            }
            // minor grid
            uno::Reference< beans::XPropertySet > xMinorGrid( xAxisSupp->getZHelpGrid(), uno::UNO_QUERY );
            if( bHasZAxisMinorGrid && xMinorGrid.is())
            {
                aPropertyStates = mxExpPropMapper->Filter( xMinorGrid );

                if( bExportContent )
                {
                    AddAutoStyleAttribute( aPropertyStates );
                    mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, XML_MINOR );
                    SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, sal_True, sal_True );
                }
                else
                {
                    CollectAutoStyle( aPropertyStates );
                }
                aPropertyStates.clear();
            }
        }
        if( pAxis )
        {
            delete pAxis;
            pAxis = NULL;
        }
    }
}

void SchXMLExportHelper::getCellAddress( sal_Int32 nCol, sal_Int32 nRow )
{
    msStringBuffer.append( (sal_Unicode)'.' );
    if( nCol < 26 )
        msStringBuffer.append( (sal_Unicode)('A' + nCol) );
    else if( nCol < 702 )
    {
        msStringBuffer.append( (sal_Unicode)('A' + nCol / 26 - 1 ));
        msStringBuffer.append( (sal_Unicode)('A' + nCol % 26) );
    }
    else
    {
        msStringBuffer.append( (sal_Unicode)('A' + nCol / 702 - 1 ));
        msStringBuffer.append( (sal_Unicode)('A' + (nCol % 702) / 26 ));
        msStringBuffer.append( (sal_Unicode)('A' + nCol % 26) );
    }

    msStringBuffer.append( nRow + (sal_Int32)1 );
}

void SchXMLExportHelper::addPosition( uno::Reference< drawing::XShape > xShape )
{
    if( xShape.is())
    {
        awt::Point aPos = xShape->getPosition();

        mrExport.GetMM100UnitConverter().convertMeasure( msStringBuffer, aPos.X );
        msString = msStringBuffer.makeStringAndClear();
        mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X, msString );

        mrExport.GetMM100UnitConverter().convertMeasure( msStringBuffer, aPos.Y );
        msString = msStringBuffer.makeStringAndClear();
        mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y, msString );
    }
}

void SchXMLExportHelper::addSize( uno::Reference< drawing::XShape > xShape )
{
    if( xShape.is())
    {
        awt::Size aSize = xShape->getSize();

        mrExport.GetMM100UnitConverter().convertMeasure( msStringBuffer, aSize.Width );
        msString = msStringBuffer.makeStringAndClear();
        mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_WIDTH,  msString );

        mrExport.GetMM100UnitConverter().convertMeasure( msStringBuffer, aSize.Height );
        msString = msStringBuffer.makeStringAndClear();
        mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_HEIGHT, msString );
    }
}

void SchXMLExportHelper::CollectAutoStyle( const ::std::vector< XMLPropertyState >& aStates )
{
    if( aStates.size())
        maAutoStyleNameQueue.push( GetAutoStylePoolP().Add( XML_STYLE_FAMILY_SCH_CHART_ID, aStates ));
}

void SchXMLExportHelper::AddAutoStyleAttribute( const ::std::vector< XMLPropertyState >& aStates )
{
    if( aStates.size())
    {
        DBG_ASSERT( ! maAutoStyleNameQueue.empty(), "Autostyle queue empty!" );

        if( ! maAutoStyleNameQueue.empty())
        {
            mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, maAutoStyleNameQueue.front());
            maAutoStyleNameQueue.pop();
        }
    }
}

void SchXMLExportHelper::exportText( const ::rtl::OUString& rText, bool bConvertTabsLFs )
{
    SvXMLElementExport aPara( mrExport, XML_NAMESPACE_TEXT,
                              ::binfilter::xmloff::token::GetXMLToken( ::binfilter::xmloff::token::XML_P ),
                              sal_True, sal_False );

    if( bConvertTabsLFs )
    {
        sal_Int32 nStartPos = 0;
        sal_Int32 nEndPos = rText.getLength();
        sal_Unicode cChar;

        for( sal_Int32 nPos = 0; nPos < nEndPos; nPos++ )
        {
            cChar = rText[ nPos ];
            switch( cChar )
            {
                case 0x0009:        // tabulator
                    {
                        if( nPos > nStartPos )
                            mrExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) );
                        nStartPos = nPos + 1;

                        SvXMLElementExport aElem( mrExport, XML_NAMESPACE_TEXT,
                                                  ::binfilter::xmloff::token::GetXMLToken( ::binfilter::xmloff::token::XML_TAB_STOP ),
                                                  sal_False, sal_False );
                    }
                    break;

                case 0x000A:        // linefeed
                    {
                        if( nPos > nStartPos )
                            mrExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) );
                        nStartPos = nPos + 1;

                        SvXMLElementExport aElem( mrExport, XML_NAMESPACE_TEXT,
                                                  ::binfilter::xmloff::token::GetXMLToken( ::binfilter::xmloff::token::XML_LINE_BREAK ),
                                                  sal_False, sal_False );
                    }
                    break;
            }
        }
        if( nEndPos > nStartPos )
        {
            if( nStartPos == 0 )
                mrExport.GetDocHandler()->characters( rText );
            else
                mrExport.GetDocHandler()->characters( rText.copy( nStartPos, (nEndPos - nStartPos)) );
        }
    }
    else // do not convert tabs and linefeeds (eg for numbers coming from unit converter)
    {
        mrExport.GetDocHandler()->characters( rText );
    }
}

// ========================================
// class SchXMLExport
// ========================================

// #110680#
SchXMLExport::SchXMLExport(
    const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory,
    sal_uInt16 nExportFlags )
:   SvXMLExport( xServiceFactory, MAP_CM, ::binfilter::xmloff::token::XML_CHART, nExportFlags ),
    maAutoStylePool( *this ),
    maExportHelper( *this, maAutoStylePool )
{
}


SchXMLExport::~SchXMLExport()
{
    // stop progress view
    if( mxStatusIndicator.is())
    {
        mxStatusIndicator->end();
        mxStatusIndicator->reset();
    }
}

void SchXMLExport::_ExportStyles( sal_Bool bUsed )
{
    SvXMLExport::_ExportStyles( bUsed );
}

void SchXMLExport::_ExportMasterStyles()
{
    // not available in chart
    DBG_WARNING( "Master Style Export requested. Not available for Chart" );
}

void SchXMLExport::_ExportAutoStyles()
{
    // there are no styles that require their own autostyles
    if( getExportFlags() & EXPORT_CONTENT )
    {
        uno::Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
        if( xChartDoc.is())
        {
            maExportHelper.collectAutoStyles( xChartDoc );
            maExportHelper.exportAutoStyles();
        }
        else
        {
            OSL_FAIL( "Couldn't export chart due to wrong XModel (must be XChartDocument)" );
        }
    }
}

void SchXMLExport::_ExportContent()
{
    uno::Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
    if( xChartDoc.is())
    {
        // add size for chart element in standalone case
        uno::Reference< drawing::XShape > xShape ( xChartDoc->getArea(), uno::UNO_QUERY );
        if( xShape.is())
        {
            awt::Size aSize = xShape->getSize();
            ::rtl::OUStringBuffer sStringBuffer;
            ::rtl::OUString sString;

            GetMM100UnitConverter().convertMeasure( sStringBuffer, aSize.Width );
            sString = sStringBuffer.makeStringAndClear();
            AddAttribute( XML_NAMESPACE_SVG, XML_WIDTH,  sString );

            GetMM100UnitConverter().convertMeasure( sStringBuffer, aSize.Height );
            sString = sStringBuffer.makeStringAndClear();
            AddAttribute( XML_NAMESPACE_SVG, XML_HEIGHT, sString );
        }

        // determine if data comes from the outside
        sal_Bool bIncludeTable = sal_True;
        uno::Reference< lang::XServiceInfo > xServ( xChartDoc, uno::UNO_QUERY );
        if( xServ.is())
        {
            if( xServ->supportsService(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart.ChartTableAddressSupplier" ))))
            {
                uno::Reference< beans::XPropertySet > xProp( xServ, uno::UNO_QUERY );
                if( xProp.is())
                {
                    uno::Any aAny;
                    try
                    {
                        ::rtl::OUString sChartAddress;
                        aAny = xProp->getPropertyValue(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ChartRangeAddress" )));
                        aAny >>= sChartAddress;
                        maExportHelper.SetChartRangeAddress( sChartAddress );

                        ::rtl::OUString sTableNumberList;
                        aAny = xProp->getPropertyValue(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TableNumberList" )));
                        aAny >>= sTableNumberList;
                        maExportHelper.SetTableNumberList( sTableNumberList );

                        // do not include own table if there are external addresses
                        bIncludeTable = (sChartAddress.getLength() == 0);
                    }
                    catch( beans::UnknownPropertyException )
                    {
                        OSL_FAIL( "Property ChartRangeAddress not supported by ChartDocument" );
                    }
                }
            }
        }
        maExportHelper.exportChart( xChartDoc, bIncludeTable );
    }
    else
    {
        OSL_FAIL( "Couldn't export chart due to wrong XModel" );
    }
}

// export components ========================================

// first version: everything goes in one storage

uno::Sequence< OUString > SAL_CALL SchXMLExport_getSupportedServiceNames() throw()
{
    const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.Chart.XMLExporter" ) );
    const uno::Sequence< OUString > aSeq( &aServiceName, 1 );
    return aSeq;
}

OUString SAL_CALL SchXMLExport_getImplementationName() throw()
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM( "SchXMLExport.Compact" ) );
}

uno::Reference< uno::XInterface > SAL_CALL SchXMLExport_createInstance(const uno::Reference< lang::XMultiServiceFactory > & rSMgr) throw( uno::Exception )
{
    // #110680#
    // #103997# removed some flags from EXPORT_ALL
    // return (cppu::OWeakObject*)new SchXMLExport( EXPORT_ALL ^ ( EXPORT_SETTINGS | EXPORT_MASTERSTYLES | EXPORT_SCRIPTS ));
    return (cppu::OWeakObject*)new SchXMLExport( rSMgr, EXPORT_ALL ^ ( EXPORT_SETTINGS | EXPORT_MASTERSTYLES | EXPORT_SCRIPTS ));
}

// ============================================================

// multiple storage version: one for content / styles / meta

uno::Sequence< OUString > SAL_CALL SchXMLExport_Styles_getSupportedServiceNames() throw()
{
    const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.Chart.XMLStylesExporter" ));
    const uno::Sequence< OUString > aSeq( &aServiceName, 1 );
    return aSeq;
}

OUString SAL_CALL SchXMLExport_Styles_getImplementationName() throw()
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM( "SchXMLExport.Styles" ));
}

uno::Reference< uno::XInterface > SAL_CALL SchXMLExport_Styles_createInstance(const uno::Reference< lang::XMultiServiceFactory >& rSMgr) throw( uno::Exception )
{
    // #110680#
    // return (cppu::OWeakObject*)new SchXMLExport( EXPORT_STYLES );
    return (cppu::OWeakObject*)new SchXMLExport( rSMgr, EXPORT_STYLES );
}

// ------------------------------------------------------------

uno::Sequence< OUString > SAL_CALL SchXMLExport_Content_getSupportedServiceNames() throw()
{
    const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.Chart.XMLContentExporter" ));
    const uno::Sequence< OUString > aSeq( &aServiceName, 1 );
    return aSeq;
}

OUString SAL_CALL SchXMLExport_Content_getImplementationName() throw()
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM( "SchXMLExport.Content" ));
}

uno::Reference< uno::XInterface > SAL_CALL SchXMLExport_Content_createInstance(const uno::Reference< lang::XMultiServiceFactory > & rSMgr) throw( uno::Exception )
{
    // #110680#
    // return (cppu::OWeakObject*)new SchXMLExport( EXPORT_AUTOSTYLES | EXPORT_CONTENT | EXPORT_FONTDECLS );
    return (cppu::OWeakObject*)new SchXMLExport( rSMgr, EXPORT_AUTOSTYLES | EXPORT_CONTENT | EXPORT_FONTDECLS );
}

// ------------------------------------------------------------

OUString SAL_CALL SchXMLExport_Meta_getImplementationName() throw()
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM( "SchXMLExport.Meta" ));
}

// XServiceInfo
OUString SAL_CALL SchXMLExport::getImplementationName() throw( uno::RuntimeException )
{
    switch( getExportFlags())
    {
        case EXPORT_ALL:
            return SchXMLExport_getImplementationName();
        case EXPORT_STYLES:
            return SchXMLExport_Styles_getImplementationName();
        case ( EXPORT_AUTOSTYLES | EXPORT_CONTENT | EXPORT_FONTDECLS ):
            return SchXMLExport_Content_getImplementationName();
        case EXPORT_META:
            return SchXMLExport_Meta_getImplementationName();

        case EXPORT_SETTINGS:
        // there is no settings component in chart
        default:
            return OUString( RTL_CONSTASCII_USTRINGPARAM( "SchXMLExport" ));
    }
}
}//end of namespace binfilter

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
