    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <DiagramHelper.hxx>
    #include <LegendHelper.hxx>
    #include <PropertyHelper.hxx>
    #include <DataSeriesHelper.hxx>
    #include <AxisHelper.hxx>
    #include <ContainerHelper.hxx>
    #include <ChartTypeHelper.hxx>
    #include <ChartModel.hxx>
    #include <ChartModelHelper.hxx>
    #include <CommonConverters.hxx>
    #include <ExplicitCategoriesProvider.hxx>
    #include <servicenames_charttypes.hxx>
    #include <RelativePositionHelper.hxx>
    #include <ControllerLockGuard.hxx>
    #include <NumberFormatterWrapper.hxx>
    #include <unonames.hxx>

    #include <com/sun/star/chart/MissingValueTreatment.hpp>
    #include <com/sun/star/chart/XChartDocument.hpp>
    #include <com/sun/star/chart/XDiagramPositioning.hpp>
    #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
    #include <com/sun/star/chart2/XTitled.hpp>
    #include <com/sun/star/chart2/XChartTypeContainer.hpp>
    #include <com/sun/star/chart2/XChartTypeTemplate.hpp>
    #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
    #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
    #include <com/sun/star/chart2/InterpretedData.hpp>
    #include <com/sun/star/chart2/AxisType.hpp>
    #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
    #include <com/sun/star/chart2/RelativePosition.hpp>
    #include <com/sun/star/chart2/RelativeSize.hpp>
    #include <com/sun/star/chart2/StackingDirection.hpp>

    #include <com/sun/star/util/CloseVetoException.hpp>
    #include <com/sun/star/util/NumberFormat.hpp>
    #include <com/sun/star/util/XModifiable.hpp>
    #include <com/sun/star/util/XNumberFormatsSupplier.hpp>

    #include <unotools/saveopt.hxx>
    #include <rtl/math.hxx>
    #include <svl/zformat.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/settings.hxx>
    #include <comphelper/sequence.hxx>
    #include <tools/diagnose_ex.h>
    #include <sal/log.hxx>

    using namespace ::com::sun::star;
    using namespace ::com::sun::star::chart2;
    using namespace ::std;

    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::uno::Sequence;
    using ::com::sun::star::uno::Any;
    using ::com::sun::star::chart2::XAnyDescriptionAccess;

    namespace chart
    {

    DiagramHelper::tTemplateWithServiceName
        DiagramHelper::getTemplateForDiagram(
            const Reference< XDiagram > & xDiagram,
            const Reference< lang::XMultiServiceFactory > & xChartTypeManager )
    {
        DiagramHelper::tTemplateWithServiceName aResult;

        if( ! (xChartTypeManager.is() && xDiagram.is()))
            return aResult;

        Sequence< OUString > aServiceNames( xChartTypeManager->getAvailableServiceNames());
        const sal_Int32 nLength = aServiceNames.getLength();

        bool bTemplateFound = false;

        for( sal_Int32 i = 0; ! bTemplateFound && i < nLength; ++i )
        {
            try
            {
                Reference< XChartTypeTemplate > xTempl(
                    xChartTypeManager->createInstance( aServiceNames[ i ] ), uno::UNO_QUERY_THROW );

                if (xTempl.is() && xTempl->matchesTemplate(xDiagram, true))
                {
                    aResult.first = xTempl;
                    aResult.second = aServiceNames[ i ];
                    bTemplateFound = true;
                }
            }
            catch( const uno::Exception & )
            {
                DBG_UNHANDLED_EXCEPTION("chart2");
            }
        }

        return aResult;
    }

    void DiagramHelper::setVertical(
        const Reference< XDiagram > & xDiagram,
        bool bVertical /* = true */ )
    {
        try
        {
            Reference< XCoordinateSystemContainer > xCnt( xDiagram, uno::UNO_QUERY );
            if (!xCnt.is())
                return;

            Sequence< Reference<XCoordinateSystem> > aCooSys = xCnt->getCoordinateSystems();
            uno::Any aValue;
            aValue <<= bVertical;
            for( sal_Int32 i=0; i<aCooSys.getLength(); ++i )
            {
                uno::Reference< XCoordinateSystem > xCooSys( aCooSys[i] );
                Reference< beans::XPropertySet > xProp( xCooSys, uno::UNO_QUERY );
                bool bChanged = false;
                if (xProp.is())
                {
                    bool bOldSwap = false;
                    if( !(xProp->getPropertyValue("SwapXAndYAxis") >>= bOldSwap)
                        || bVertical != bOldSwap )
                        bChanged = true;

                    if( bChanged )
                        xProp->setPropertyValue("SwapXAndYAxis", aValue);
                }

                if (!xCooSys.is())
                    continue;

                const sal_Int32 nDimensionCount = xCooSys->getDimension();
                sal_Int32 nDimIndex = 0;
                for (nDimIndex=0; nDimIndex < nDimensionCount; ++nDimIndex)
                {
                    const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nDimIndex);
                    for (sal_Int32 nI = 0; nI <= nMaximumScaleIndex; ++nI)
                    {
                        Reference<chart2::XAxis> xAxis = xCooSys->getAxisByDimension(nDimIndex,nI);
                        if (!xAxis.is())
                            continue;

                        //adapt title rotation only when axis swapping has changed
                        if (!bChanged)
                            continue;

                        Reference< XTitled > xTitled( xAxis, uno::UNO_QUERY );
                        if (!xTitled.is())
                            continue;

                        Reference< beans::XPropertySet > xTitleProps( xTitled->getTitleObject(), uno::UNO_QUERY );
                        if (!xTitleProps.is())
                            continue;

                        double fAngleDegree = 0.0;
                        xTitleProps->getPropertyValue("TextRotation") >>= fAngleDegree;
                        if (fAngleDegree != 0.0 &&
                            !rtl::math::approxEqual(fAngleDegree, 90.0))
                            continue;

                        double fNewAngleDegree = 0.0;
                        if( !bVertical && nDimIndex == 1 )
                            fNewAngleDegree = 90.0;
                        else if( bVertical && nDimIndex == 0 )
                            fNewAngleDegree = 90.0;

                        xTitleProps->setPropertyValue("TextRotation", uno::Any(fNewAngleDegree));
                    }
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }
    }

    bool DiagramHelper::getVertical( const uno::Reference< chart2::XDiagram > & xDiagram,
                                 bool& rbFound, bool& rbAmbiguous )
    {
        bool bValue = false;
        rbFound = false;
        rbAmbiguous = false;

        Reference< XCoordinateSystemContainer > xCnt( xDiagram, uno::UNO_QUERY );
        if (!xCnt.is())
            return false;

        Sequence< Reference<XCoordinateSystem> > aCooSys = xCnt->getCoordinateSystems();

        for (sal_Int32 i = 0; i < aCooSys.getLength(); ++i)
        {
            Reference<beans::XPropertySet> xProp(aCooSys[i], uno::UNO_QUERY);
            if (!xProp.is())
                continue;

            bool bCurrent = false;
            if (xProp->getPropertyValue("SwapXAndYAxis") >>= bCurrent)
            {
                if (!rbFound)
                {
                    bValue = bCurrent;
                    rbFound = true;
                }
                else if (bCurrent != bValue)
                {
                    // ambiguous -> choose always first found
                    rbAmbiguous = true;
                }
            }
        }
        return bValue;
    }

    void DiagramHelper::setStackMode(
        const Reference< XDiagram > & xDiagram,
        StackMode eStackMode
    )
    {
        try
        {
            bool bValueFound = false;
            bool bIsAmbiguous = false;
            StackMode eOldStackMode = DiagramHelper::getStackMode( xDiagram, bValueFound, bIsAmbiguous );

            if( eStackMode == eOldStackMode && !bIsAmbiguous )
                return;

            StackingDirection eNewDirection = StackingDirection_NO_STACKING;
            if( eStackMode == StackMode::YStacked || eStackMode == StackMode::YStackedPercent )
                eNewDirection = StackingDirection_Y_STACKING;
            else if( eStackMode == StackMode::ZStacked )
                eNewDirection = StackingDirection_Z_STACKING;

            uno::Any aNewDirection( eNewDirection );

            bool bPercent = false;
            if( eStackMode == StackMode::YStackedPercent )
                bPercent = true;

            //iterate through all coordinate systems
            uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
            if( !xCooSysContainer.is() )
                return;
            uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
            for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
            {
                uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] );
                //set correct percent stacking
                const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(1);
                for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI)
                {
                    Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( 1,nI ));
                    if( xAxis.is())
                    {
                        chart2::ScaleData aScaleData = xAxis->getScaleData();
                        if( (aScaleData.AxisType==AxisType::PERCENT) != bPercent )
                        {
                            if( bPercent )
                                aScaleData.AxisType = AxisType::PERCENT;
                            else
                                aScaleData.AxisType = AxisType::REALNUMBER;
                            xAxis->setScaleData( aScaleData );
                        }
                    }
                }
                //iterate through all chart types in the current coordinate system
                uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
                if( !xChartTypeContainer.is() )
                    continue;
                uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
                sal_Int32 nMax = aChartTypeList.getLength();
                if( nMax >= 1 )
                    nMax = 1;
                for( sal_Int32 nT = 0; nT < nMax; ++nT )
                {
                    uno::Reference< XChartType > xChartType( aChartTypeList[nT] );

                    //iterate through all series in this chart type
                    uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY );
                    OSL_ASSERT( xDataSeriesContainer.is());
                    if( !xDataSeriesContainer.is() )
                        continue;

                    uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );
                    for( sal_Int32 nS = 0; nS < aSeriesList.getLength(); ++nS )
                    {
                        Reference< beans::XPropertySet > xProp( aSeriesList[nS], uno::UNO_QUERY );
                        if(xProp.is())
                            xProp->setPropertyValue( "StackingDirection", aNewDirection );
                    }
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }
    }

    StackMode DiagramHelper::getStackMode( const Reference< XDiagram > & xDiagram, bool& rbFound, bool& rbAmbiguous )
    {
        rbFound=false;
        rbAmbiguous=false;

        StackMode eGlobalStackMode = StackMode::NONE;

        //iterate through all coordinate systems
        uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
        if( !xCooSysContainer.is() )
            return eGlobalStackMode;
        uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
        for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
        {
            uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] );

            //iterate through all chart types in the current coordinate system
            uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
            if( !xChartTypeContainer.is() )
                continue;
            uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
            for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT )
            {
                uno::Reference< XChartType > xChartType( aChartTypeList[nT] );

                StackMode eLocalStackMode = DiagramHelper::getStackModeFromChartType(
                    xChartType, rbFound, rbAmbiguous, xCooSys );

                if( rbFound && eLocalStackMode != eGlobalStackMode && nT>0 )
                {
                    rbAmbiguous = true;
                    return eGlobalStackMode;
                }

                eGlobalStackMode = eLocalStackMode;
            }
        }

        return eGlobalStackMode;
    }

    StackMode DiagramHelper::getStackModeFromChartType(
        const Reference< XChartType > & xChartType,
        bool& rbFound, bool& rbAmbiguous,
        const Reference< XCoordinateSystem > & xCorrespondingCoordinateSystem )
    {
        StackMode eStackMode = StackMode::NONE;
        rbFound = false;
        rbAmbiguous = false;

        try
        {
            Reference< XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY_THROW );
            Sequence< Reference< chart2::XDataSeries > > aSeries( xDSCnt->getDataSeries());

            chart2::StackingDirection eCommonDirection = chart2::StackingDirection_NO_STACKING;
            bool bDirectionInitialized = false;

            // first series is irrelevant for stacking, start with second, unless
            // there is only one series
            const sal_Int32 nSeriesCount = aSeries.getLength();
            sal_Int32 i = (nSeriesCount == 1) ? 0: 1;
            for( ; i<nSeriesCount; ++i )
            {
                rbFound = true;
                Reference< beans::XPropertySet > xProp( aSeries[i], uno::UNO_QUERY_THROW );
                chart2::StackingDirection eCurrentDirection = eCommonDirection;
                // property is not MAYBEVOID
                bool bSuccess = ( xProp->getPropertyValue( "StackingDirection" ) >>= eCurrentDirection );
                OSL_ASSERT( bSuccess );
                if( ! bDirectionInitialized )
                {
                    eCommonDirection = eCurrentDirection;
                    bDirectionInitialized = true;
                }
                else
                {
                    if( eCommonDirection != eCurrentDirection )
                    {
                        rbAmbiguous = true;
                        break;
                    }
                }
            }

            if( rbFound )
            {
                if( eCommonDirection == chart2::StackingDirection_Z_STACKING )
                    eStackMode = StackMode::ZStacked;
                else if( eCommonDirection == chart2::StackingDirection_Y_STACKING )
                {
                    eStackMode = StackMode::YStacked;

                    // percent stacking
                    if( xCorrespondingCoordinateSystem.is() )
                    {
                        if( 1 < xCorrespondingCoordinateSystem->getDimension() )
                        {
                            sal_Int32 nAxisIndex = 0;
                            if( nSeriesCount )
                                nAxisIndex = DataSeriesHelper::getAttachedAxisIndex(aSeries[0]);

                            Reference< chart2::XAxis > xAxis(
                                xCorrespondingCoordinateSystem->getAxisByDimension( 1,nAxisIndex ));
                            if( xAxis.is())
                            {
                                chart2::ScaleData aScaleData = xAxis->getScaleData();
                                if( aScaleData.AxisType==chart2::AxisType::PERCENT )
                                    eStackMode = StackMode::YStackedPercent;
                            }
                        }
                    }
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }

        return eStackMode;
    }

    sal_Int32 DiagramHelper::getDimension( const Reference< XDiagram > & xDiagram )
    {
        // -1: not yet set
        sal_Int32 nResult = -1;

        try
        {
            Reference< XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY );
            if( xCooSysCnt.is() )
            {
                Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
                    xCooSysCnt->getCoordinateSystems());

                for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i )
                {
                    Reference< XCoordinateSystem > xCooSys( aCooSysSeq[i] );
                    if(xCooSys.is())
                    {
                        nResult = xCooSys->getDimension();
                        break;
                    }
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }

        return nResult;
    }

    void DiagramHelper::setDimension(
        const Reference< XDiagram > & xDiagram,
        sal_Int32 nNewDimensionCount )
    {
        if( ! xDiagram.is())
            return;

        if( DiagramHelper::getDimension( xDiagram ) == nNewDimensionCount )
            return;

        try
        {
            bool rbFound = false;
            bool rbAmbiguous = true;
            StackMode eStackMode = DiagramHelper::getStackMode( xDiagram, rbFound, rbAmbiguous );
            bool bIsSupportingOnlyDeepStackingFor3D=false;

            //change all coordinate systems:
            Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY_THROW );
            Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
            for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
            {
                Reference< XCoordinateSystem > xOldCooSys( aCooSysList[nCS], uno::UNO_QUERY );
                Reference< XCoordinateSystem > xNewCooSys;

                Reference< XChartTypeContainer > xChartTypeContainer( xOldCooSys, uno::UNO_QUERY );
                if( !xChartTypeContainer.is() )
                    continue;

                Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
                for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT )
                {
                    Reference< XChartType > xChartType( aChartTypeList[nT], uno::UNO_QUERY );
                    bIsSupportingOnlyDeepStackingFor3D = ChartTypeHelper::isSupportingOnlyDeepStackingFor3D( xChartType );
                    if(!xNewCooSys.is())
                    {
                        xNewCooSys = xChartType->createCoordinateSystem( nNewDimensionCount );
                        break;
                    }
                    //@todo make sure that all following charttypes are also capable of the new dimension
                    //otherwise separate them in a different group
                    //BM: might be done in replaceCoordinateSystem()
                }

                // replace the old coordinate system at all places where it was used
                DiagramHelper::replaceCoordinateSystem( xDiagram, xOldCooSys, xNewCooSys );
            }

            //correct stack mode if necessary
            if( nNewDimensionCount==3 && eStackMode != StackMode::ZStacked && bIsSupportingOnlyDeepStackingFor3D )
                DiagramHelper::setStackMode( xDiagram, StackMode::ZStacked );
            else if( nNewDimensionCount==2 && eStackMode == StackMode::ZStacked )
                DiagramHelper::setStackMode( xDiagram, StackMode::NONE );
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }
    }

    void DiagramHelper::replaceCoordinateSystem(
        const Reference< XDiagram > & xDiagram,
        const Reference< XCoordinateSystem > & xCooSysToReplace,
        const Reference< XCoordinateSystem > & xReplacement )
    {
        OSL_ASSERT( xDiagram.is());
        if( ! xDiagram.is())
            return;

        // update the coordinate-system container
        Reference< XCoordinateSystemContainer > xCont( xDiagram, uno::UNO_QUERY );
        if( xCont.is())
        {
            try
            {
                Reference< chart2::data::XLabeledDataSequence > xCategories = DiagramHelper::getCategoriesFromDiagram( xDiagram );

                // move chart types of xCooSysToReplace to xReplacement
                Reference< XChartTypeContainer > xCTCntCooSys( xCooSysToReplace, uno::UNO_QUERY_THROW );
                Reference< XChartTypeContainer > xCTCntReplacement( xReplacement, uno::UNO_QUERY_THROW );
                xCTCntReplacement->setChartTypes( xCTCntCooSys->getChartTypes());

                xCont->removeCoordinateSystem( xCooSysToReplace );
                xCont->addCoordinateSystem( xReplacement );

                if( xCategories.is() )
                    DiagramHelper::setCategoriesToDiagram( xCategories, xDiagram );
            }
            catch( const uno::Exception & )
            {
                DBG_UNHANDLED_EXCEPTION("chart2");
            }
        }
    }

    bool DiagramHelper::isSeriesAttachedToMainAxis(
                              const uno::Reference< chart2::XDataSeries >& xDataSeries )
    {
        sal_Int32 nAxisIndex = DataSeriesHelper::getAttachedAxisIndex(xDataSeries);
        return (nAxisIndex==0);
    }

    bool DiagramHelper::attachSeriesToAxis( bool bAttachToMainAxis
                            , const uno::Reference< chart2::XDataSeries >& xDataSeries
                            , const uno::Reference< chart2::XDiagram >& xDiagram
                            , const uno::Reference< uno::XComponentContext > & xContext
                            , bool bAdaptAxes )
    {
        bool bChanged = false;

        //set property at axis
        Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );

        sal_Int32 nNewAxisIndex = bAttachToMainAxis ? 0 : 1;
        sal_Int32 nOldAxisIndex = DataSeriesHelper::getAttachedAxisIndex(xDataSeries);
        uno::Reference< chart2::XAxis > xOldAxis( DiagramHelper::getAttachedAxis( xDataSeries, xDiagram ) );

        if( nOldAxisIndex != nNewAxisIndex )
        {
            try
            {
                xProp->setPropertyValue( "AttachedAxisIndex", uno::Any( nNewAxisIndex ) );
                bChanged = true;
            }
            catch( const uno::Exception & )
            {
                DBG_UNHANDLED_EXCEPTION("chart2");
            }
        }

        if( bChanged && xDiagram.is() )
        {
            uno::Reference< XAxis > xAxis( AxisHelper::getAxis( 1, bAttachToMainAxis, xDiagram ) );
            if(!xAxis.is()) //create an axis if necessary
                xAxis = AxisHelper::createAxis( 1, bAttachToMainAxis, xDiagram, xContext );
            if( bAdaptAxes )
            {
                AxisHelper::makeAxisVisible( xAxis );
                AxisHelper::hideAxisIfNoDataIsAttached( xOldAxis, xDiagram );
            }
        }

        return bChanged;
    }

    uno::Reference< XAxis > DiagramHelper::getAttachedAxis(
            const uno::Reference< XDataSeries >& xSeries,
            const uno::Reference< XDiagram >& xDiagram )
    {
        return AxisHelper::getAxis( 1, DiagramHelper::isSeriesAttachedToMainAxis( xSeries ), xDiagram );
    }

    uno::Reference< XChartType > DiagramHelper::getChartTypeOfSeries(
                                    const uno::Reference< chart2::XDiagram >&   xDiagram
                                  , const uno::Reference< XDataSeries >&        xGivenDataSeries )
    {
        if( !xGivenDataSeries.is() )
            return nullptr;
        if(!xDiagram.is())
            return nullptr;

        //iterate through the model to find the given xSeries
        //the found parent indicates the charttype

        //iterate through all coordinate systems
        uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
        if( !xCooSysContainer.is())
            return nullptr;

        uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
        for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
        {
            uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] );

            //iterate through all chart types in the current coordinate system
            uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
            OSL_ASSERT( xChartTypeContainer.is());
            if( !xChartTypeContainer.is() )
                continue;
            uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
            for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT )
            {
                uno::Reference< XChartType > xChartType( aChartTypeList[nT] );

                //iterate through all series in this chart type
                uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY );
                OSL_ASSERT( xDataSeriesContainer.is());
                if( !xDataSeriesContainer.is() )
                    continue;

                uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );
                for( sal_Int32 nS = 0; nS < aSeriesList.getLength(); ++nS )
                {
                    if( xGivenDataSeries==aSeriesList[nS] )
                        return xChartType;
                }
            }
        }
        return nullptr;
    }

    std::vector< Reference< XDataSeries > >
        DiagramHelper::getDataSeriesFromDiagram(
            const Reference< XDiagram > & xDiagram )
    {
        std::vector< Reference< XDataSeries > > aResult;

        try
        {
            Reference< XCoordinateSystemContainer > xCooSysCnt(
                xDiagram, uno::UNO_QUERY_THROW );
            Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
                xCooSysCnt->getCoordinateSystems());
            for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i )
            {
                Reference< XChartTypeContainer > xCTCnt( aCooSysSeq[i], uno::UNO_QUERY_THROW );
                Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes());
                for( sal_Int32 j=0; j<aChartTypeSeq.getLength(); ++j )
                {
                    Reference< XDataSeriesContainer > xDSCnt( aChartTypeSeq[j], uno::UNO_QUERY_THROW );
                    Sequence< Reference< XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries() );
                    std::copy( aSeriesSeq.begin(), aSeriesSeq.end(),
                                 std::back_inserter( aResult ));
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }

        return aResult;
    }

    Sequence< Sequence< Reference< XDataSeries > > >
            DiagramHelper::getDataSeriesGroups( const Reference< XDiagram > & xDiagram )
    {
        vector< Sequence< Reference< XDataSeries > > > aResult;

        //iterate through all coordinate systems
        Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
        if( xCooSysContainer.is() )
        {
            Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
            for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
            {
                //iterate through all chart types in the current coordinate system
                Reference< XChartTypeContainer > xChartTypeContainer( aCooSysList[nCS], uno::UNO_QUERY );
                if( !xChartTypeContainer.is() )
                    continue;
                Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
                for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT )
                {
                    Reference< XDataSeriesContainer > xDataSeriesContainer( aChartTypeList[nT], uno::UNO_QUERY );
                    if( !xDataSeriesContainer.is() )
                        continue;
                    aResult.push_back( xDataSeriesContainer->getDataSeries() );
                }
            }
        }
        return comphelper::containerToSequence( aResult );
    }

    Reference< XChartType >
        DiagramHelper::getChartTypeByIndex( const Reference< XDiagram >& xDiagram, sal_Int32 nIndex )
    {
        Reference< XChartType > xChartType;

        //iterate through all coordinate systems
        Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
        if( ! xCooSysContainer.is())
            return xChartType;

        Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
        sal_Int32 nTypesSoFar = 0;
        for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
        {
            Reference< XChartTypeContainer > xChartTypeContainer( aCooSysList[nCS], uno::UNO_QUERY );
            if( !xChartTypeContainer.is() )
                continue;
            Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
            if( nIndex >= 0 && nIndex < (nTypesSoFar + aChartTypeList.getLength()) )
            {
                xChartType.set( aChartTypeList[nIndex - nTypesSoFar] );
                break;
            }
            nTypesSoFar += aChartTypeList.getLength();
        }

        return xChartType;
    }

    namespace
    {

    std::vector< Reference< XAxis > > lcl_getAxisHoldingCategoriesFromDiagram(
        const Reference< XDiagram > & xDiagram )
    {
        std::vector< Reference< XAxis > > aRet;

        // return first x-axis as fall-back
        Reference< XAxis > xFallBack;
        try
        {
            Reference< XCoordinateSystemContainer > xCooSysCnt(
                xDiagram, uno::UNO_QUERY_THROW );
            Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
                xCooSysCnt->getCoordinateSystems());
            for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i )
            {
                Reference< XCoordinateSystem > xCooSys( aCooSysSeq[i] );
                OSL_ASSERT( xCooSys.is());
                for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
                {
                    const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
                    for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI)
                    {
                        Reference< XAxis > xAxis = xCooSys->getAxisByDimension( nN,nI );
                        OSL_ASSERT( xAxis.is());
                        if( xAxis.is())
                        {
                            ScaleData aScaleData = xAxis->getScaleData();
                            if( aScaleData.Categories.is() || (aScaleData.AxisType == AxisType::CATEGORY) )
                            {
                                aRet.push_back(xAxis);
                            }
                            if( (nN == 0) && !xFallBack.is())
                                xFallBack.set( xAxis );
                        }
                    }
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2" );
        }

        if( aRet.empty() )
            aRet.push_back(xFallBack);

        return aRet;
    }

    } // anonymous namespace

    bool DiagramHelper::isCategoryDiagram(
                const Reference< XDiagram >& xDiagram )
    {
        try
        {
            Reference< XCoordinateSystemContainer > xCooSysCnt(
                xDiagram, uno::UNO_QUERY_THROW );
            Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
                xCooSysCnt->getCoordinateSystems());
            for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i )
            {
                Reference< XCoordinateSystem > xCooSys( aCooSysSeq[i] );
                OSL_ASSERT( xCooSys.is());
                for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
                {
                    const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
                    for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI)
                    {
                        Reference< XAxis > xAxis = xCooSys->getAxisByDimension( nN,nI );
                        OSL_ASSERT( xAxis.is());
                        if( xAxis.is())
                        {
                            ScaleData aScaleData = xAxis->getScaleData();
                            if( aScaleData.AxisType == AxisType::CATEGORY || aScaleData.AxisType == AxisType::DATE )
                                return true;
                        }
                    }
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }

        return false;
    }

    void DiagramHelper::setCategoriesToDiagram(
        const Reference< chart2::data::XLabeledDataSequence >& xCategories,
        const Reference< XDiagram >& xDiagram,
        bool bSetAxisType  /* = false */,
        bool bCategoryAxis /* = true */ )
    {
        std::vector< Reference< chart2::XAxis > > aCatAxes(
            lcl_getAxisHoldingCategoriesFromDiagram( xDiagram ));

        for (auto const& elem : aCatAxes)
        {
            Reference< chart2::XAxis > xCatAxis(elem);
            if( xCatAxis.is())
            {
                ScaleData aScaleData( xCatAxis->getScaleData());
                aScaleData.Categories = xCategories;
                if( bSetAxisType )
                {
                    if( bCategoryAxis )
                        aScaleData.AxisType = AxisType::CATEGORY;
                    else if( aScaleData.AxisType == AxisType::CATEGORY || aScaleData.AxisType == AxisType::DATE )
                        aScaleData.AxisType = AxisType::REALNUMBER;
                }
                xCatAxis->setScaleData( aScaleData );
            }
        }
    }

    Reference< data::XLabeledDataSequence >
        DiagramHelper::getCategoriesFromDiagram(
            const Reference< XDiagram > & xDiagram )
    {
        Reference< data::XLabeledDataSequence > xResult;

        try
        {
            std::vector< Reference< chart2::XAxis > > aCatAxes(
                lcl_getAxisHoldingCategoriesFromDiagram( xDiagram ));
            //search for first categories
            if (!aCatAxes.empty())
            {
                Reference< chart2::XAxis > xCatAxis(aCatAxes[0]);
                if( xCatAxis.is())
                {
                    ScaleData aScaleData( xCatAxis->getScaleData());
                    if( aScaleData.Categories.is() )
                    {
                        xResult.set( aScaleData.Categories );
                        uno::Reference<beans::XPropertySet> xProp(aScaleData.Categories->getValues(), uno::UNO_QUERY );
                        if( xProp.is() )
                        {
                            try
                            {
                                xProp->setPropertyValue( "Role", uno::Any( OUString("categories") ) );
                            }
                            catch( const uno::Exception & )
                            {
                                DBG_UNHANDLED_EXCEPTION("chart2");
                            }
                        }
                    }
                }
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }

        return xResult;
    }

    void lcl_generateAutomaticCategoriesFromChartType(
                Sequence< OUString >& rRet,
                const Reference< XChartType >& xChartType )
    {
        if(!xChartType.is())
            return;
        OUString aMainSeq( xChartType->getRoleOfSequenceForSeriesLabel() );
            Reference< XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY );
        if( xSeriesCnt.is() )
        {
            Sequence< Reference< XDataSeries > > aSeriesSeq( xSeriesCnt->getDataSeries() );
            for( sal_Int32 nS = 0; nS < aSeriesSeq.getLength(); nS++ )
            {
                Reference< data::XDataSource > xDataSource( aSeriesSeq[nS], uno::UNO_QUERY );
                if( !xDataSource.is() )
                    continue;
                Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
                    ::chart::DataSeriesHelper::getDataSequenceByRole( xDataSource, aMainSeq ));
                if( !xLabeledSeq.is() )
                    continue;
                Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues() );
                if( !xValueSeq.is() )
                    continue;
                rRet = xValueSeq->generateLabel( chart2::data::LabelOrigin_LONG_SIDE );
                if( rRet.getLength() )
                    return;
            }
        }
    }

    Sequence< OUString > DiagramHelper::generateAutomaticCategoriesFromCooSys( const Reference< XCoordinateSystem > & xCooSys )
    {
        Sequence< OUString > aRet;

        Reference< XChartTypeContainer > xTypeCntr( xCooSys, uno::UNO_QUERY );
        if( xTypeCntr.is() )
        {
            Sequence< Reference< XChartType > > aChartTypes( xTypeCntr->getChartTypes() );
            for( sal_Int32 nN=0; nN<aChartTypes.getLength(); nN++ )
            {
                lcl_generateAutomaticCategoriesFromChartType( aRet, aChartTypes[nN] );
                if( aRet.getLength() )
                    return aRet;
            }
        }
        return aRet;
    }

    Sequence< OUString > DiagramHelper::getExplicitSimpleCategories(
                ChartModel& rModel )
    {
        uno::Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( rModel ) );
        ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSys, rModel );
        return aExplicitCategoriesProvider.getSimpleCategories();
    }

    namespace
    {
    void lcl_switchToDateCategories( const Reference< XChartDocument >& xChartDoc, const Reference< XAxis >& xAxis )
    {
        if( !xAxis.is() )
            return;
        if( !xChartDoc.is() )
            return;

        ScaleData aScale( xAxis->getScaleData() );
        if( xChartDoc->hasInternalDataProvider() )
        {
            //remove all content the is not of type double and remove multiple level
            Reference< XAnyDescriptionAccess > xDataAccess( xChartDoc->getDataProvider(), uno::UNO_QUERY );
            if( xDataAccess.is() )
            {
                Sequence< Sequence< Any > > aAnyCategories( xDataAccess->getAnyRowDescriptions() );
                double fTest = 0.0;
                double fNan = 0.0;
                ::rtl::math::setNan( & fNan );
                sal_Int32 nN = aAnyCategories.getLength();
                for( ; nN--; )
                {
                    Sequence< Any >& rCat = aAnyCategories[nN];
                    if( rCat.getLength() > 1 )
                        rCat.realloc(1);
                    if( rCat.getLength() == 1 )
                    {
                        Any& rAny = rCat[0];
                        if( !(rAny>>=fTest) )
                        {
                            rAny <<= fNan;
                        }
                    }
                }
                xDataAccess->setAnyRowDescriptions( aAnyCategories );
            }
            //check the numberformat at the axis
            Reference< beans::XPropertySet > xAxisProps( xAxis, uno::UNO_QUERY );
            Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( xChartDoc, uno::UNO_QUERY );
            if( xAxisProps.is() && xNumberFormatsSupplier.is() )
            {
                sal_Int32 nNumberFormat = -1;
                xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormat;

                Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() );
                if( xNumberFormats.is() )
                {
                    Reference< beans::XPropertySet > xKeyProps;
                    try
                    {
                        xKeyProps = xNumberFormats->getByKey( nNumberFormat );
                    }
                    catch( const uno::Exception & )
                    {
                        DBG_UNHANDLED_EXCEPTION("chart2");
                    }
                    sal_Int32 nType = util::NumberFormat::UNDEFINED;
                    if( xKeyProps.is() )
                        xKeyProps->getPropertyValue( "Type" ) >>= nType;
                    if( !( nType & util::NumberFormat::DATE ) )
                    {
                        //set a date format to the axis
                        const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
                        Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::DATE,  rLocaleDataWrapper.getLanguageTag().getLocale(), true/*bCreate*/ );
                        if( aKeySeq.getLength() )
                        {
                            xAxisProps->setPropertyValue(CHART_UNONAME_NUMFMT, uno::Any(aKeySeq[0]));
                        }
                    }
                }
            }
        }
        if( aScale.AxisType != chart2::AxisType::DATE )
            AxisHelper::removeExplicitScaling( aScale );
        aScale.AxisType = chart2::AxisType::DATE;
        xAxis->setScaleData( aScale );
    }

    void lcl_switchToTextCategories( const Reference< XChartDocument >& xChartDoc, const Reference< XAxis >& xAxis )
    {
        if( !xAxis.is() )
            return;
        if( !xChartDoc.is() )
            return;
        ScaleData aScale( xAxis->getScaleData() );
        if( aScale.AxisType != chart2::AxisType::CATEGORY )
            AxisHelper::removeExplicitScaling( aScale );
        //todo migrate dates to text?
        aScale.AxisType = chart2::AxisType::CATEGORY;
        aScale.AutoDateAxis = false;
        xAxis->setScaleData( aScale );
    }

    }

    void DiagramHelper::switchToDateCategories( const Reference< XChartDocument >& xChartDoc )
    {
        Reference< frame::XModel > xChartModel( xChartDoc, uno::UNO_QUERY );
        if(xChartModel.is())
        {
            ControllerLockGuardUNO aCtrlLockGuard( xChartModel );

            Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( xChartModel ) );
            if( xCooSys.is() )
            {
                Reference< XAxis > xAxis( xCooSys->getAxisByDimension(0,0) );
                lcl_switchToDateCategories( xChartDoc, xAxis );
            }
        }
    }

    void DiagramHelper::switchToTextCategories( const Reference< XChartDocument >& xChartDoc )
    {
        Reference< frame::XModel > xChartModel( xChartDoc, uno::UNO_QUERY );
        if(xChartModel.is())
        {
            ControllerLockGuardUNO aCtrlLockGuard( xChartModel );

            Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( xChartModel ) );
            if( xCooSys.is() )
            {
                Reference< XAxis > xAxis( xCooSys->getAxisByDimension(0,0) );
                lcl_switchToTextCategories( xChartDoc, xAxis );
            }
        }
    }

    bool DiagramHelper::isSupportingDateAxis( const Reference< chart2::XDiagram >& xDiagram )
    {
        return ::chart::ChartTypeHelper::isSupportingDateAxis(
                DiagramHelper::getChartTypeByIndex( xDiagram, 0 ), 0 );
    }

    bool DiagramHelper::isDateNumberFormat( sal_Int32 nNumberFormat, const Reference< util::XNumberFormats >& xNumberFormats )
    {
        bool bIsDate = false;
        if( !xNumberFormats.is() )
            return bIsDate;

        Reference< beans::XPropertySet > xKeyProps = xNumberFormats->getByKey( nNumberFormat );
        if( xKeyProps.is() )
        {
            sal_Int32 nType = util::NumberFormat::UNDEFINED;
            xKeyProps->getPropertyValue( "Type" ) >>= nType;
            bIsDate = nType & util::NumberFormat::DATE;
        }
        return bIsDate;
    }

    sal_Int32 DiagramHelper::getDateNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier )
    {
        sal_Int32 nRet=-1;

        //try to get a date format with full year display
        const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
        NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier );
        SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter();
        if( pNumFormatter )
        {
            nRet = pNumFormatter->GetFormatIndex( NF_DATE_SYS_DDMMYYYY, rLanguageTag.getLanguageType() );
        }
        else
        {
            Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() );
            if( xNumberFormats.is() )
            {
                Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::DATE,
                        rLanguageTag.getLocale(), true/*bCreate */);
                if( aKeySeq.getLength() )
                {
                    nRet = aKeySeq[0];
                }
            }
        }
        return nRet;
    }

    sal_Int32 DiagramHelper::getDateTimeInputNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier, double fNumber )
    {
        sal_Int32 nRet = 0;

        // Get the most detailed date/time format according to fNumber.
        NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier );
        SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter();
        if (!pNumFormatter)
            SAL_WARN("chart2", "DiagramHelper::getDateTimeInputNumberFormat - no SvNumberFormatter");
        else
        {
            SvNumFormatType nType;
            // Obtain best matching date, time or datetime format.
            nRet = pNumFormatter->GuessDateTimeFormat( nType, fNumber, LANGUAGE_SYSTEM);
            // Obtain the corresponding edit format.
            nRet = pNumFormatter->GetEditFormat( fNumber, nRet, nType, LANGUAGE_SYSTEM, nullptr);
        }
        return nRet;
    }

    sal_Int32 DiagramHelper::getPercentNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier )
    {
        sal_Int32 nRet=-1;
        const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
        NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier );
        SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter();
        if( pNumFormatter )
        {
            nRet = pNumFormatter->GetStandardFormat( SvNumFormatType::PERCENT, rLanguageTag.getLanguageType() );
        }
        else
        {
            Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() );
            if( xNumberFormats.is() )
            {
                Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::PERCENT,
                        rLanguageTag.getLocale(), true/*bCreate*/ );
                if( aKeySeq.getLength() )
                {
                    nRet = aKeySeq[0];
                }
            }
        }
        return nRet;
    }

    Sequence< Reference< XChartType > >
        DiagramHelper::getChartTypesFromDiagram(
            const Reference< XDiagram > & xDiagram )
    {
        std::vector< Reference< XChartType > > aResult;

        if(xDiagram.is())
        {
            try
            {
                Reference< XCoordinateSystemContainer > xCooSysCnt(
                    xDiagram, uno::UNO_QUERY_THROW );
                Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
                    xCooSysCnt->getCoordinateSystems());
                for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i )
                {
                    Reference< XChartTypeContainer > xCTCnt( aCooSysSeq[i], uno::UNO_QUERY_THROW );
                    Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes());
                    std::copy( aChartTypeSeq.begin(), aChartTypeSeq.end(),
                                 std::back_inserter( aResult ));
                }
            }
            catch( const uno::Exception & )
            {
                DBG_UNHANDLED_EXCEPTION("chart2");
            }
        }

        return comphelper::containerToSequence( aResult );
    }

    bool DiagramHelper::areChartTypesCompatible( const Reference< ::chart2::XChartType >& xFirstType,
                    const Reference< ::chart2::XChartType >& xSecondType )
    {
        if( !xFirstType.is() || !xSecondType.is() )
            return false;

        std::vector< OUString > aFirstRoles( ContainerHelper::SequenceToVector( xFirstType->getSupportedMandatoryRoles() ) );
        std::vector< OUString > aSecondRoles( ContainerHelper::SequenceToVector( xSecondType->getSupportedMandatoryRoles() ) );
        std::sort( aFirstRoles.begin(), aFirstRoles.end() );
        std::sort( aSecondRoles.begin(), aSecondRoles.end() );
        return ( aFirstRoles == aSecondRoles );
    }

    namespace
    {
         /**
         * This method implements the logic of checking if a series can be moved
         * forward/backward. Depending on the "bDoMove" parameter the series will
         * be moved (bDoMove = true) or the function just will test if the
         * series can be moved without doing the move (bDoMove = false).
         *
         * @param xDiagram
         *  Reference to the diagram that contains the series.
         *
         * @param xGivenDataSeries
         *  Reference to the series that should moved or tested for moving.
         *
         * @param bForward
         *  Direction in which the series should be moved or tested for moving.
         *
         * @param bDoMove
         *  Should this function really move the series (true) or just test if it is
         *  possible (false).
         *
         *
         * @returns
         *  in case of bDoMove == true
         *      - True : if the move was done
         *      - False : the move failed
         *  in case of bDoMove == false
         *      - True : the series can be moved
         *      - False : the series can not be moved
         *
         */

    bool lcl_moveSeriesOrCheckIfMoveIsAllowed(
        const Reference< XDiagram >& xDiagram,
        const Reference< XDataSeries >& xGivenDataSeries,
        bool bForward,
        bool bDoMove )
    {
        bool bMovedOrMoveAllowed = false;

        try
        {
            uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );

            if( xGivenDataSeries.is() && xCooSysContainer.is() )
            {
                //find position of series.
                bool bFound = false;
                uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );

                for( sal_Int32 nCS = 0; !bFound && nCS < aCooSysList.getLength(); ++nCS )
                {
                    uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] );

                    //iterate through all chart types in the current coordinate system
                    uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
                    OSL_ASSERT( xChartTypeContainer.is());
                    if( !xChartTypeContainer.is() )
                        continue;
                    uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
                    uno::Reference< XChartType > xFormerChartType;

                    for( sal_Int32 nT = 0; !bFound && nT < aChartTypeList.getLength(); ++nT )
                    {
                        uno::Reference< XChartType > xCurrentChartType( aChartTypeList[nT] );

                        //iterate through all series in this chart type
                        uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xCurrentChartType, uno::UNO_QUERY );
                        OSL_ASSERT( xDataSeriesContainer.is());
                        if( !xDataSeriesContainer.is() )
                            continue;

                        uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );

                        for( sal_Int32 nS = 0; !bFound && nS < aSeriesList.getLength(); ++nS )
                        {

                            // We found the series we are interested in!
                            if( xGivenDataSeries==aSeriesList[nS] )
                            {
                                sal_Int32 nOldSeriesIndex = nS;
                                bFound = true;

                                try
                                {
                                    sal_Int32 nNewSeriesIndex = nS;

                                    if( bForward )
                                        nNewSeriesIndex--;
                                    else
                                        nNewSeriesIndex++;

                                    if( nNewSeriesIndex >= 0 && nNewSeriesIndex < aSeriesList.getLength() )
                                    {
                                        //move series in the same charttype
                                        bMovedOrMoveAllowed = true;
                                        if( bDoMove )
                                        {
                                            aSeriesList[ nOldSeriesIndex ] = aSeriesList[ nNewSeriesIndex ];
                                            aSeriesList[ nNewSeriesIndex ] = xGivenDataSeries;
                                            xDataSeriesContainer->setDataSeries( aSeriesList );
                                        }
                                    }
                                    else if( nNewSeriesIndex<0 )
                                    {
                                        //exchange series with former charttype
                                        if( xFormerChartType.is() && DiagramHelper::areChartTypesCompatible( xFormerChartType, xCurrentChartType ) )
                                        {
                                            bMovedOrMoveAllowed = true;
                                            if( bDoMove )
                                            {
                                                uno::Reference< XDataSeriesContainer > xOtherDataSeriesContainer( xFormerChartType, uno::UNO_QUERY );
                                                if( xOtherDataSeriesContainer.is() )
                                                {
                                                    uno::Sequence< uno::Reference< XDataSeries > > aOtherSeriesList( xOtherDataSeriesContainer->getDataSeries() );
                                                    sal_Int32 nOtherSeriesIndex = aOtherSeriesList.getLength()-1;
                                                    if( nOtherSeriesIndex >= 0 && nOtherSeriesIndex < aOtherSeriesList.getLength() )
                                                    {
                                                        uno::Reference< XDataSeries > xExchangeSeries( aOtherSeriesList[nOtherSeriesIndex] );
                                                        aOtherSeriesList[nOtherSeriesIndex] = xGivenDataSeries;
                                                        xOtherDataSeriesContainer->setDataSeries(aOtherSeriesList);

                                                        aSeriesList[nOldSeriesIndex]=xExchangeSeries;
                                                        xDataSeriesContainer->setDataSeries(aSeriesList);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else if( nT+1 < aChartTypeList.getLength() )
                                    {
                                        //exchange series with next charttype
                                        uno::Reference< XChartType > xOtherChartType( aChartTypeList[nT+1] );
                                        if( xOtherChartType.is() && DiagramHelper::areChartTypesCompatible( xOtherChartType, xCurrentChartType ) )
                                        {
                                            bMovedOrMoveAllowed = true;
                                            if( bDoMove )
                                            {
                                                uno::Reference< XDataSeriesContainer > xOtherDataSeriesContainer( xOtherChartType, uno::UNO_QUERY );
                                                if( xOtherDataSeriesContainer.is() )
                                                {
                                                    uno::Sequence< uno::Reference< XDataSeries > > aOtherSeriesList( xOtherDataSeriesContainer->getDataSeries() );
                                                    sal_Int32 nOtherSeriesIndex = 0;
                                                    if( nOtherSeriesIndex >= 0 && nOtherSeriesIndex < aOtherSeriesList.getLength() )
                                                    {
                                                        uno::Reference< XDataSeries > xExchangeSeries( aOtherSeriesList[nOtherSeriesIndex] );
                                                        aOtherSeriesList[nOtherSeriesIndex] = xGivenDataSeries;
                                                        xOtherDataSeriesContainer->setDataSeries(aOtherSeriesList);

                                                        aSeriesList[nOldSeriesIndex]=xExchangeSeries;
                                                        xDataSeriesContainer->setDataSeries(aSeriesList);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                catch( const util::CloseVetoException& )
                                {
                                }
                                catch( const uno::RuntimeException& )
                                {
                                }
                            }
                        }
                        xFormerChartType = xCurrentChartType;
                    }
                }
            }
        }
        catch( const util::CloseVetoException& )
        {
        }
        catch( const uno::RuntimeException& )
        {
        }
        return bMovedOrMoveAllowed;
    }
    } // anonymous namespace

    bool DiagramHelper::isSeriesMoveable(
        const Reference< XDiagram >& xDiagram,
        const Reference< XDataSeries >& xGivenDataSeries,
        bool bForward )
    {
        const bool bDoMove = false;

        bool bIsMoveable = lcl_moveSeriesOrCheckIfMoveIsAllowed(
            xDiagram, xGivenDataSeries, bForward, bDoMove );

        return bIsMoveable;
    }

    bool DiagramHelper::moveSeries( const Reference< XDiagram >& xDiagram, const Reference< XDataSeries >& xGivenDataSeries, bool bForward )
    {
        const bool bDoMove = true;

        bool bMoved = lcl_moveSeriesOrCheckIfMoveIsAllowed(
            xDiagram, xGivenDataSeries, bForward, bDoMove );

        return bMoved;
    }

    bool DiagramHelper::isSupportingFloorAndWall( const Reference<
                    chart2::XDiagram >& xDiagram )
    {
        //pies and donuts currently do not support this because of wrong files from older versions
        //todo: allow this in future again, if fileversion are available for ole objects (metastream)
        //thus the wrong bottom can be removed on import

        Sequence< Reference< chart2::XChartType > > aTypes(
                ::chart::DiagramHelper::getChartTypesFromDiagram( xDiagram ) );
        for( sal_Int32 nN = 0; nN < aTypes.getLength(); nN++ )
        {
            Reference< chart2::XChartType > xType( aTypes[nN] );
            if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
                return false;
            if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
                return false;
            if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
                return false;
        }
        return true;
    }

    bool DiagramHelper::isPieOrDonutChart( const css::uno::Reference< css::chart2::XDiagram >& xDiagram )
    {
        uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex(
            xDiagram, 0 ) );

        if( xChartType .is() )
        {
            OUString aChartType = xChartType->getChartType();
            if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
                return true;
        }
        return false;
    }

    sal_Int32 DiagramHelper::getGeometry3D(
        const uno::Reference< chart2::XDiagram > & xDiagram,
        bool& rbFound, bool& rbAmbiguous )
    {
        sal_Int32 nCommonGeom( DataPointGeometry3D::CUBOID );
        rbFound = false;
        rbAmbiguous = false;

        std::vector< Reference< chart2::XDataSeries > > aSeriesVec(
            DiagramHelper::getDataSeriesFromDiagram( xDiagram ));

        if( aSeriesVec.empty())
            rbAmbiguous = true;

        for (auto const& series : aSeriesVec)
        {
            try
            {
                sal_Int32 nGeom = 0;
                Reference< beans::XPropertySet > xProp(series, uno::UNO_QUERY_THROW);
                if( xProp->getPropertyValue( "Geometry3D") >>= nGeom )
                {
                    if( ! rbFound )
                    {
                        // first series
                        nCommonGeom = nGeom;
                        rbFound = true;
                    }
                    // further series: compare for uniqueness
                    else if( nCommonGeom != nGeom )
                    {
                        rbAmbiguous = true;
                        break;
                    }
                }
            }
            catch( const uno::Exception & )
            {
                DBG_UNHANDLED_EXCEPTION("chart2");
            }
        }

        return nCommonGeom;
    }

    void DiagramHelper::setGeometry3D(
        const Reference< chart2::XDiagram > & xDiagram,
        sal_Int32 nNewGeometry )
    {
        std::vector< Reference< chart2::XDataSeries > > aSeriesVec(
            DiagramHelper::getDataSeriesFromDiagram( xDiagram ));

        for (auto const& series : aSeriesVec)
        {
            DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints(
                series, "Geometry3D", uno::Any( nNewGeometry ));
        }
    }

    sal_Int32 DiagramHelper::getCorrectedMissingValueTreatment(
                const Reference< chart2::XDiagram > & xDiagram,
                const Reference< chart2::XChartType >& xChartType )
    {
        sal_Int32 nResult = css::chart::MissingValueTreatment::LEAVE_GAP;
        uno::Sequence < sal_Int32 > aAvailableMissingValueTreatments(
                    ChartTypeHelper::getSupportedMissingValueTreatments( xChartType ) );

        uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY );
        if( xDiaProp.is() && (xDiaProp->getPropertyValue( "MissingValueTreatment" ) >>= nResult) )
        {
            //ensure that the set value is supported by this charttype
            for( sal_Int32 nN = 0; nN < aAvailableMissingValueTreatments.getLength(); nN++ )
                if( aAvailableMissingValueTreatments[nN] == nResult )
                    return nResult; //ok
        }

        //otherwise use the first supported one
        if( aAvailableMissingValueTreatments.getLength() )
        {
            nResult = aAvailableMissingValueTreatments[0];
            return nResult;
        }

        return nResult;
    }

    DiagramPositioningMode DiagramHelper::getDiagramPositioningMode( const uno::Reference<
                    chart2::XDiagram > & xDiagram )
    {
        DiagramPositioningMode eMode = DiagramPositioningMode_AUTO;
        uno::Reference< beans::XPropertySet > xDiaProps( xDiagram, uno::UNO_QUERY );
        if( xDiaProps.is() )
        {
            RelativePosition aRelPos;
            RelativeSize aRelSize;
            if( (xDiaProps->getPropertyValue("RelativePosition") >>= aRelPos ) &&
                (xDiaProps->getPropertyValue("RelativeSize") >>= aRelSize ) )
            {
                bool bPosSizeExcludeAxes=false;
                xDiaProps->getPropertyValue("PosSizeExcludeAxes") >>= bPosSizeExcludeAxes;
                if( bPosSizeExcludeAxes )
                    eMode = DiagramPositioningMode_EXCLUDING;
                else
                    eMode = DiagramPositioningMode_INCLUDING;
            }
        }
        return eMode;
    }

    void lcl_ensureRange0to1( double& rValue )
    {
        if(rValue<0.0)
            rValue=0.0;
        if(rValue>1.0)
            rValue=1.0;
    }

    bool DiagramHelper::setDiagramPositioning( const uno::Reference< frame::XModel >& xChartModel,
            const awt::Rectangle& rPosRect /*100th mm*/ )
    {
        ControllerLockGuardUNO aCtrlLockGuard( xChartModel );

        bool bChanged = false;
        awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) );
        uno::Reference< beans::XPropertySet > xDiaProps( ChartModelHelper::findDiagram( xChartModel ), uno::UNO_QUERY );
        if( !xDiaProps.is() )
            return bChanged;

        RelativePosition aOldPos;
        RelativeSize aOldSize;
        xDiaProps->getPropertyValue("RelativePosition" ) >>= aOldPos;
        xDiaProps->getPropertyValue("RelativeSize" ) >>= aOldSize;

        RelativePosition aNewPos;
        aNewPos.Anchor = drawing::Alignment_TOP_LEFT;
        aNewPos.Primary = double(rPosRect.X)/double(aPageSize.Width);
        aNewPos.Secondary = double(rPosRect.Y)/double(aPageSize.Height);

        chart2::RelativeSize aNewSize;
        aNewSize.Primary = double(rPosRect.Width)/double(aPageSize.Width);
        aNewSize.Secondary = double(rPosRect.Height)/double(aPageSize.Height);

        lcl_ensureRange0to1( aNewPos.Primary );
        lcl_ensureRange0to1( aNewPos.Secondary );
        lcl_ensureRange0to1( aNewSize.Primary );
        lcl_ensureRange0to1( aNewSize.Secondary );
        if( (aNewPos.Primary + aNewSize.Primary) > 1.0 )
            aNewPos.Primary = 1.0 - aNewSize.Primary;
        if( (aNewPos.Secondary + aNewSize.Secondary) > 1.0 )
            aNewPos.Secondary = 1.0 - aNewSize.Secondary;

        xDiaProps->setPropertyValue( "RelativePosition", uno::Any(aNewPos) );
        xDiaProps->setPropertyValue( "RelativeSize", uno::Any(aNewSize) );

        bChanged = (aOldPos.Anchor!=aNewPos.Anchor) ||
            (aOldPos.Primary!=aNewPos.Primary) ||
            (aOldPos.Secondary!=aNewPos.Secondary) ||
            (aOldSize.Primary!=aNewSize.Primary) ||
            (aOldSize.Secondary!=aNewSize.Secondary);
        return bChanged;
    }

    awt::Rectangle DiagramHelper::getDiagramRectangleFromModel( const uno::Reference< frame::XModel >& xChartModel )
    {
        awt::Rectangle aRet(-1,-1,-1,-1);

        uno::Reference< beans::XPropertySet > xDiaProps( ChartModelHelper::findDiagram( xChartModel ), uno::UNO_QUERY );
        if( !xDiaProps.is() )
            return aRet;

        awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) );

        RelativePosition aRelPos;
        RelativeSize aRelSize;
        xDiaProps->getPropertyValue("RelativePosition" ) >>= aRelPos;
        xDiaProps->getPropertyValue("RelativeSize" ) >>= aRelSize;

        awt::Size aAbsSize(
            static_cast< sal_Int32 >( aRelSize.Primary * aPageSize.Width ),
            static_cast< sal_Int32 >( aRelSize.Secondary * aPageSize.Height ));

        awt::Point aAbsPos(
            static_cast< sal_Int32 >( aRelPos.Primary * aPageSize.Width ),
            static_cast< sal_Int32 >( aRelPos.Secondary * aPageSize.Height ));

        awt::Point aAbsPosLeftTop = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject( aAbsPos, aAbsSize, aRelPos.Anchor );

        aRet = awt::Rectangle(aAbsPosLeftTop.X, aAbsPosLeftTop.Y, aAbsSize.Width, aAbsSize.Height );

        return aRet;
    }

    bool DiagramHelper::switchDiagramPositioningToExcludingPositioning(
        ChartModel& rModel, bool bResetModifiedState, bool bConvertAlsoFromAutoPositioning )
    {
        //return true if something was changed
        const SvtSaveOptions::ODFDefaultVersion nCurrentODFVersion( SvtSaveOptions().GetODFDefaultVersion() );
        if( nCurrentODFVersion > SvtSaveOptions::ODFVER_012 )
        {
            uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( rModel.getFirstDiagram(), uno::UNO_QUERY );
            if( xDiagramPositioning.is() && ( bConvertAlsoFromAutoPositioning || !xDiagramPositioning->isAutomaticDiagramPositioning() )
                    && !xDiagramPositioning->isExcludingDiagramPositioning() )
            {
                ControllerLockGuard aCtrlLockGuard( rModel );
                bool bModelWasModified = rModel.isModified();
                xDiagramPositioning->setDiagramPositionExcludingAxes( xDiagramPositioning->calculateDiagramPositionExcludingAxes() );
                if(bResetModifiedState && !bModelWasModified )
                    rModel.setModified(false);
                return true;
            }
        }
        return false;
    }

    } //  namespace chart

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

↑ V1008 Consider inspecting the 'for' operator. No more than one iteration of the loop will be performed.

↑ V1019 Compound assignment expression is used inside condition.

↑ V560 A part of conditional expression is always true: nOtherSeriesIndex >= 0.

↑ V1019 Compound assignment expression is used inside condition.