/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmloff.hxx"

#include "SchXMLAxisContext.hxx"
#include "SchXMLChartContext.hxx"
#include "SchXMLTools.hxx"
#include <xmloff/xmlnmspe.hxx>
#include <xmloff/xmlement.hxx>
#include <xmloff/xmlstyle.hxx>
#include <xmloff/prstylei.hxx>
#include <xmloff/nmspmap.hxx>
#include <xmloff/xmluconv.hxx>

#include <tools/debug.hxx>

#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
#include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
#include <com/sun/star/chart/ChartAxisPosition.hpp>
#include <com/sun/star/chart/ChartAxisType.hpp>
#include <com/sun/star/chart/TimeIncrement.hpp>
#include <com/sun/star/chart/TimeInterval.hpp>
#include <com/sun/star/chart/TimeUnit.hpp>
#include <com/sun/star/chart/XAxis.hpp>
#include <com/sun/star/chart/XAxisSupplier.hpp>
#include <com/sun/star/chart2/AxisType.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>

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

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

using rtl::OUString;
using com::sun::star::uno::Reference;

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

static SvXMLEnumMapEntry aXMLAxisDimensionMap[] =
{
    { XML_X,  SCH_XML_AXIS_X  },
    { XML_Y,  SCH_XML_AXIS_Y  },
    { XML_Z,  SCH_XML_AXIS_Z  },
    { XML_TOKEN_INVALID, 0 }
};

static SvXMLEnumMapEntry aXMLAxisTypeMap[] =
{
    { XML_AUTO,  ::com::sun::star::chart::ChartAxisType::AUTOMATIC },
    { XML_TEXT,  ::com::sun::star::chart::ChartAxisType::CATEGORY },
    { XML_DATE,  ::com::sun::star::chart::ChartAxisType::DATE },
    { XML_TOKEN_INVALID, 0 }
};

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

class SchXMLCategoriesContext : public SvXMLImportContext
{
private:
	SchXMLImportHelper& m_rImportHelper;
	OUString& mrAddress;

public:
	SchXMLCategoriesContext( SchXMLImportHelper& rImpHelper,
								   SvXMLImport& rImport,
								   sal_uInt16 nPrefix,
								   const OUString& rLocalName,
								   OUString& rAddress );
	virtual ~SchXMLCategoriesContext();
	virtual void StartElement( const Reference< ::com::sun::star::xml::sax::XAttributeList >& xAttrList );
};

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


class DateScaleContext : public SvXMLImportContext
{
public:
	DateScaleContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport,
                        sal_uInt16 nPrefix, const OUString& rLocalName,
                        const Reference< beans::XPropertySet > xAxisProps );

	virtual ~DateScaleContext();
	virtual void StartElement( const Reference< ::com::sun::star::xml::sax::XAttributeList >& xAttrList );

private:
	SchXMLImportHelper& m_rImportHelper;
	Reference< beans::XPropertySet > m_xAxisProps;
};


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

SchXMLAxisContext::SchXMLAxisContext( SchXMLImportHelper& rImpHelper,
									  SvXMLImport& rImport, const OUString& rLocalName,
									  Reference< chart::XDiagram > xDiagram,
									  std::vector< SchXMLAxis >& rAxes,
                                      OUString & rCategoriesAddress,
                                      bool bAddMissingXAxisForNetCharts,
                                      bool bAdaptWrongPercentScaleValues,
                                      bool bAdaptXAxisOrientationForOld2DBarCharts,
                                      bool& rbAxisPositionAttributeImported ) :
		SvXMLImportContext( rImport, XML_NAMESPACE_CHART, rLocalName ),
		m_rImportHelper( rImpHelper ),
		m_xDiagram( xDiagram ),
		m_rAxes( rAxes ),
        m_rCategoriesAddress( rCategoriesAddress ),
        m_nAxisType(chart::ChartAxisType::AUTOMATIC),
        m_bAxisTypeImported(false),
        m_bDateScaleImported(false),
        m_bAddMissingXAxisForNetCharts( bAddMissingXAxisForNetCharts ),
        m_bAdaptWrongPercentScaleValues( bAdaptWrongPercentScaleValues ),
        m_bAdaptXAxisOrientationForOld2DBarCharts( bAdaptXAxisOrientationForOld2DBarCharts ),
        m_rbAxisPositionAttributeImported( rbAxisPositionAttributeImported )
{
}

SchXMLAxisContext::~SchXMLAxisContext()
{}

Reference< chart::XAxis > lcl_getChartAxis( SchXMLAxis aCurrentAxis, const Reference< chart::XDiagram > xDiagram )
{
    Reference< chart::XAxis > xAxis;
    Reference< chart::XAxisSupplier > xAxisSuppl( xDiagram, uno::UNO_QUERY );
    if( !xAxisSuppl.is() )
        return xAxis;
    if( aCurrentAxis.nAxisIndex == 0 )
        xAxis = xAxisSuppl->getAxis(aCurrentAxis.eDimension);
    else
        xAxis = xAxisSuppl->getSecondaryAxis(aCurrentAxis.eDimension);
    return xAxis;
}

/* returns a shape for the current axis's title. The property
   "Has...AxisTitle" is set to "True" to get the shape
 */
Reference< drawing::XShape > SchXMLAxisContext::getTitleShape()
{
	Reference< drawing::XShape > xResult;
    Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
    Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
    if( !xDiaProp.is() || !xAxis.is() )
        return xResult;

    rtl::OUString aPropName;
    switch( m_aCurrentAxis.eDimension )
	{
		case SCH_XML_AXIS_X:
            if( m_aCurrentAxis.nAxisIndex == 0 )
                aPropName = OUString::createFromAscii( "HasXAxisTitle" );
            else
                aPropName = OUString::createFromAscii( "HasSecondaryXAxisTitle" );
    		break;
		case SCH_XML_AXIS_Y:
			if( m_aCurrentAxis.nAxisIndex == 0 )
                aPropName = OUString::createFromAscii( "HasYAxisTitle" );
            else
                aPropName = OUString::createFromAscii( "HasSecondaryYAxisTitle" );
    		break;
		case SCH_XML_AXIS_Z:
            aPropName = OUString::createFromAscii( "HasZAxisTitle" );
        	break;
        case SCH_XML_AXIS_UNDEF:
            DBG_ERROR( "Invalid axis" );
            break;
	}
    xDiaProp->setPropertyValue( aPropName, uno::makeAny(sal_True) );
    xResult = Reference< drawing::XShape >( xAxis->getAxisTitle(), uno::UNO_QUERY );
	return xResult;
}

void SchXMLAxisContext::CreateGrid( OUString sAutoStyleName, bool bIsMajor )
{
    Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
    Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
    if( !xDiaProp.is() || !xAxis.is() )
        return;

    rtl::OUString aPropName;
    switch( m_aCurrentAxis.eDimension )
	{
		case SCH_XML_AXIS_X:
            if( bIsMajor )
                aPropName = OUString::createFromAscii("HasXAxisGrid");
            else
                aPropName = OUString::createFromAscii("HasXAxisHelpGrid");
			break;
		case SCH_XML_AXIS_Y:
			if( bIsMajor )
                aPropName = OUString::createFromAscii("HasYAxisGrid");
            else
                aPropName = OUString::createFromAscii("HasYAxisHelpGrid");
    		break;
		case SCH_XML_AXIS_Z:
            if( bIsMajor )
                aPropName = OUString::createFromAscii("HasZAxisGrid");
            else
                aPropName = OUString::createFromAscii("HasZAxisHelpGrid");
        	break;
        case SCH_XML_AXIS_UNDEF:
            DBG_ERROR( "Invalid axis" );
            break;
	}
    xDiaProp->setPropertyValue( aPropName, uno::makeAny(sal_True) );

    Reference< beans::XPropertySet > xGridProp;
    if( bIsMajor )
        xGridProp = xAxis->getMajorGrid();
    else
        xGridProp = xAxis->getMinorGrid();
	
    // set properties
	if( xGridProp.is())
	{
        // the line color is black as default, in the model it is a light gray
        xGridProp->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "LineColor" )),
                                     uno::makeAny( COL_BLACK ));
        if( sAutoStyleName.getLength())
        {
            const SvXMLStylesContext* pStylesCtxt = m_rImportHelper.GetAutoStylesContext();
            if( pStylesCtxt )
            {
                const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext(
                    m_rImportHelper.GetChartFamilyID(), sAutoStyleName );

                if( pStyle && pStyle->ISA( XMLPropStyleContext ))
                    (( XMLPropStyleContext* )pStyle )->FillPropertySet( xGridProp );
            }
        }
    }
}

namespace
{
enum AxisAttributeTokens
{
    XML_TOK_AXIS_DIMENSION,
    XML_TOK_AXIS_NAME,
    XML_TOK_AXIS_STYLE_NAME,
    XML_TOK_AXIS_TYPE,
    XML_TOK_AXIS_TYPE_EXT
};

SvXMLTokenMapEntry aAxisAttributeTokenMap[] =
{
    { XML_NAMESPACE_CHART,      XML_DIMENSION,  XML_TOK_AXIS_DIMENSION      },
    { XML_NAMESPACE_CHART,      XML_NAME,       XML_TOK_AXIS_NAME           },
    { XML_NAMESPACE_CHART,      XML_STYLE_NAME, XML_TOK_AXIS_STYLE_NAME     },
    { XML_NAMESPACE_CHART,      XML_AXIS_TYPE,  XML_TOK_AXIS_TYPE           },
    { XML_NAMESPACE_CHART_EXT,  XML_AXIS_TYPE,  XML_TOK_AXIS_TYPE_EXT       },
    XML_TOKEN_MAP_END
};

class AxisAttributeTokenMap : public SvXMLTokenMap
{
public:
    AxisAttributeTokenMap(): SvXMLTokenMap( aAxisAttributeTokenMap ) {}
    virtual ~AxisAttributeTokenMap() {}
};

//a AxisAttributeTokenMap Singleton
struct theAxisAttributeTokenMap : public rtl::Static< AxisAttributeTokenMap, theAxisAttributeTokenMap > {};
}

void SchXMLAxisContext::StartElement( const Reference< xml::sax::XAttributeList >& xAttrList )
{
	// parse attributes
	sal_Int16 nAttrCount = xAttrList.is()? xAttrList->getLength(): 0;
	SchXMLImport& rImport = ( SchXMLImport& )GetImport();
    const SvXMLTokenMap& rAttrTokenMap = theAxisAttributeTokenMap::get();

	for( sal_Int16 i = 0; i < nAttrCount; i++ )
	{
		OUString sAttrName = xAttrList->getNameByIndex( i );
		OUString aLocalName;
		OUString aValue = xAttrList->getValueByIndex( i );
		sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );

		switch( rAttrTokenMap.Get( nPrefix, aLocalName ))
		{
			case XML_TOK_AXIS_DIMENSION:
				{
					sal_uInt16 nEnumVal;
					if( rImport.GetMM100UnitConverter().convertEnum( nEnumVal, aValue, aXMLAxisDimensionMap ))
						m_aCurrentAxis.eDimension = ( SchXMLAxisDimension )nEnumVal;
				}
				break;
			case XML_TOK_AXIS_NAME:
				m_aCurrentAxis.aName = aValue;
				break;
            case XML_TOK_AXIS_TYPE:
            case XML_TOK_AXIS_TYPE_EXT:
                sal_uInt16 nEnumVal;
                if( rImport.GetMM100UnitConverter().convertEnum( nEnumVal, aValue, aXMLAxisTypeMap ))
                {
                    m_nAxisType = nEnumVal;
					m_bAxisTypeImported = true;
                }
                break;
			case XML_TOK_AXIS_STYLE_NAME:
				m_aAutoStyleName = aValue;
				break;
		}
	}

	// check for number of axes with same dimension
	m_aCurrentAxis.nAxisIndex = 0;
	sal_Int32 nNumOfAxes = m_rAxes.size();
	for( sal_Int32 nCurrent = 0; nCurrent < nNumOfAxes; nCurrent++ )
	{
		if( m_rAxes[ nCurrent ].eDimension == m_aCurrentAxis.eDimension )
			m_aCurrentAxis.nAxisIndex++;
	}
    CreateAxis();
}
namespace
{

Reference< chart2::XAxis > lcl_getAxis( const Reference< frame::XModel >& xChartModel,
                                            sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
{
    Reference< chart2::XAxis > xAxis;

    try
    {
        Reference< chart2::XChartDocument > xChart2Document( xChartModel, uno::UNO_QUERY );
        if( xChart2Document.is() )
        {
            Reference< chart2::XDiagram > xDiagram( xChart2Document->getFirstDiagram());
            Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW );
            uno::Sequence< Reference< chart2::XCoordinateSystem > >
                aCooSysSeq( xCooSysCnt->getCoordinateSystems());
            sal_Int32 nCooSysIndex = 0;
            if( nCooSysIndex < aCooSysSeq.getLength() )
            {
                Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIndex] );
                if( xCooSys.is() && nDimensionIndex < xCooSys->getDimension() )
                {
                    const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
                    if( nAxisIndex <= nMaxAxisIndex )
                        xAxis = xCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex );
                }
            }
        }
    }
    catch( uno::Exception & )
	{
		DBG_ERROR( "Couldn't get axis" );
	}

    return xAxis;
}

bool lcl_divideBy100( uno::Any& rDoubleAny )
{
    bool bChanged = false;
    double fValue=0.0;
    if( (rDoubleAny>>=fValue) && (fValue!=0.0) )
    {
        fValue/=100.0;
        rDoubleAny = uno::makeAny(fValue);
        bChanged = true;
    }
    return bChanged;
}

bool lcl_AdaptWrongPercentScaleValues(chart2::ScaleData& rScaleData)
{
    bool bChanged = lcl_divideBy100( rScaleData.Minimum );
    bChanged = lcl_divideBy100( rScaleData.Maximum ) || bChanged;
    bChanged = lcl_divideBy100( rScaleData.Origin ) || bChanged;
    bChanged = lcl_divideBy100( rScaleData.IncrementData.Distance ) || bChanged;
    return bChanged;
}

}//end anonymous namespace

void SchXMLAxisContext::CreateAxis()
{
    m_rAxes.push_back( m_aCurrentAxis );

    Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
    if( !xDiaProp.is() )
        return;
    rtl::OUString aPropName;
    switch( m_aCurrentAxis.eDimension )
	{
		case SCH_XML_AXIS_X:
            if( m_aCurrentAxis.nAxisIndex == 0 )
                aPropName = OUString::createFromAscii("HasXAxis");
            else
                aPropName = OUString::createFromAscii("HasSecondaryXAxis");
			break;
		case SCH_XML_AXIS_Y:
			if( m_aCurrentAxis.nAxisIndex == 0 )
                aPropName = OUString::createFromAscii("HasYAxis");
            else
                aPropName = OUString::createFromAscii("HasSecondaryYAxis");
    		break;
		case SCH_XML_AXIS_Z:
            if( m_aCurrentAxis.nAxisIndex == 0 )
                aPropName = OUString::createFromAscii("HasXAxis");
            else
                aPropName = OUString::createFromAscii("HasSecondaryXAxis");
        	break;
        case SCH_XML_AXIS_UNDEF:
            DBG_ERROR( "Invalid axis" );
            break;
    }
    try
    {
        xDiaProp->setPropertyValue( aPropName, uno::makeAny(sal_True) );
    }
    catch( beans::UnknownPropertyException & )
    {
        DBG_ERROR( "Couldn't turn on axis" );
    }
    if( m_aCurrentAxis.eDimension==SCH_XML_AXIS_Z )
    {
        bool bSettingZAxisSuccedded = false;
        try
        {
            xDiaProp->getPropertyValue( aPropName ) >>= bSettingZAxisSuccedded;
        }
        catch( beans::UnknownPropertyException & )
        {
            DBG_ERROR( "Couldn't turn on z axis" );
        }
        if( !bSettingZAxisSuccedded )
            return;
    }

    
    m_xAxisProps = Reference<beans::XPropertySet>( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ), uno::UNO_QUERY );
    
    if( m_bAddMissingXAxisForNetCharts && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y && m_aCurrentAxis.nAxisIndex==0 )
    {
        try
        {
            xDiaProp->setPropertyValue( OUString::createFromAscii( "HasXAxis" ), uno::makeAny(sal_True) );
        }
        catch( beans::UnknownPropertyException & )
        {
            DBG_ERROR( "Couldn't turn on x axis" );
        }
    }

	// set properties
	if( m_xAxisProps.is())
	{
        uno::Any aTrueBool( uno::makeAny( sal_True ));
        uno::Any aFalseBool( uno::makeAny( sal_False ));

        // #i109879# the line color is black as default, in the model it is a light gray
        m_xAxisProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "LineColor" )),
                                     uno::makeAny( COL_BLACK ));

        m_xAxisProps->setPropertyValue( OUString::createFromAscii( "DisplayLabels" ), aFalseBool );

        // #88077# AutoOrigin 'on' is default
        m_xAxisProps->setPropertyValue( OUString::createFromAscii( "AutoOrigin" ), aTrueBool );

        if( m_bAxisTypeImported )
            m_xAxisProps->setPropertyValue( OUString::createFromAscii( "AxisType" ), uno::makeAny(m_nAxisType) );
        
        if( m_aAutoStyleName.getLength())
        {
            const SvXMLStylesContext* pStylesCtxt = m_rImportHelper.GetAutoStylesContext();
            if( pStylesCtxt )
            {
                const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext(
                    m_rImportHelper.GetChartFamilyID(), m_aAutoStyleName );

                if( pStyle && pStyle->ISA( XMLPropStyleContext ))
                {
                    // note: SvXMLStyleContext::FillPropertySet is not const
                    XMLPropStyleContext * pPropStyleContext = const_cast< XMLPropStyleContext * >( dynamic_cast< const XMLPropStyleContext * >( pStyle ));
                    if( pPropStyleContext )
                        pPropStyleContext->FillPropertySet( m_xAxisProps );

                    if( m_bAdaptWrongPercentScaleValues && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y )
                    {
                        //set scale data of added x axis back to default
                        Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(),
                                            m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) );
                        if( xAxis.is() )
                        {
                            chart2::ScaleData aScaleData( xAxis->getScaleData());
                            if( lcl_AdaptWrongPercentScaleValues(aScaleData) )
                                xAxis->setScaleData( aScaleData );
                        }
                    }

                    if( m_bAddMissingXAxisForNetCharts )
                    {
                        //copy style from y axis to added x axis:
                        
                        Reference< chart::XAxisSupplier > xAxisSuppl( xDiaProp, uno::UNO_QUERY );
		                if( xAxisSuppl.is() )
                        {
                            Reference< beans::XPropertySet > xXAxisProp( xAxisSuppl->getAxis(0), uno::UNO_QUERY );
                            (( XMLPropStyleContext* )pStyle )->FillPropertySet( xXAxisProp );
                        }

                        //set scale data of added x axis back to default
                        Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(),
                                            0 /*nDimensionIndex*/, 0 /*nAxisIndex*/ ) );
                        if( xAxis.is() )
                        {
                            chart2::ScaleData aScaleData;
                            aScaleData.AxisType = chart2::AxisType::CATEGORY;
                            aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL;
                            xAxis->setScaleData( aScaleData );
                        }

                        //set line style of added x axis to invisible
                        Reference< beans::XPropertySet > xNewAxisProp( xAxis, uno::UNO_QUERY );
                        if( xNewAxisProp.is() )
                        {
                            xNewAxisProp->setPropertyValue( OUString::createFromAscii("LineStyle")
                                , uno::makeAny(drawing::LineStyle_NONE));
                        }
                    }

                    if( m_bAdaptXAxisOrientationForOld2DBarCharts && m_aCurrentAxis.eDimension == SCH_XML_AXIS_X )
                    {
                        bool bIs3DChart = false;
                        if( xDiaProp.is() && ( xDiaProp->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("Dim3D"))) >>= bIs3DChart ) 
                            && !bIs3DChart )
                        {	
                            Reference< chart2::XChartDocument > xChart2Document( GetImport().GetModel(), uno::UNO_QUERY );
                            if( xChart2Document.is() )
                            {
                                Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xChart2Document->getFirstDiagram(), uno::UNO_QUERY );
                                if( xCooSysCnt.is() )
                                {
                                    uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() );
                                    if( aCooSysSeq.getLength() )
                                    {
                                        bool bSwapXandYAxis = false;
                                        Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] );
                                        Reference< beans::XPropertySet > xCooSysProp( xCooSys, uno::UNO_QUERY );
                                        if( xCooSysProp.is() && ( xCooSysProp->getPropertyValue( OUString(RTL_CONSTASCII_USTRINGPARAM("SwapXAndYAxis"))) >>= bSwapXandYAxis )
                                            && bSwapXandYAxis )
                                        {
                                            Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( 0, m_aCurrentAxis.nAxisIndex );
                                            if( xAxis.is() )
                                            {
                                                chart2::ScaleData aScaleData = xAxis->getScaleData();
                                                aScaleData.Orientation = chart2::AxisOrientation_REVERSE;
                                                xAxis->setScaleData( aScaleData );
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    m_rbAxisPositionAttributeImported = m_rbAxisPositionAttributeImported || SchXMLTools::getPropertyFromContext(
                        OUString(RTL_CONSTASCII_USTRINGPARAM("CrossoverPosition")), pPropStyleContext, pStylesCtxt ).hasValue();
                }
            }
        }
    }
}

void SchXMLAxisContext::SetAxisTitle()
{
    if( !m_aCurrentAxis.aTitle.getLength() )
        return;

    Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
    if( !xAxis.is() )
        return;

    Reference< beans::XPropertySet > xTitleProp( xAxis->getAxisTitle() );
    if( xTitleProp.is() )
    {
        try
        {
            xTitleProp->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "String" )), uno::makeAny(m_aCurrentAxis.aTitle) );
        }
        catch( beans::UnknownPropertyException & )
        {
            DBG_ERROR( "Property String for Title not available" );
        }
    }
}

//-----------------------------------------------------------------------
namespace
{
enum AxisChildTokens
{
    XML_TOK_AXIS_TITLE,
    XML_TOK_AXIS_CATEGORIES,
    XML_TOK_AXIS_GRID,
    XML_TOK_AXIS_DATE_SCALE,
    XML_TOK_AXIS_DATE_SCALE_EXT
};

SvXMLTokenMapEntry aAxisChildTokenMap[] =
{
    { XML_NAMESPACE_CHART,      XML_TITLE,              XML_TOK_AXIS_TITLE          },
	{ XML_NAMESPACE_CHART,      XML_CATEGORIES,         XML_TOK_AXIS_CATEGORIES     },
	{ XML_NAMESPACE_CHART,      XML_GRID,               XML_TOK_AXIS_GRID           },
    { XML_NAMESPACE_CHART,      XML_DATE_SCALE,         XML_TOK_AXIS_DATE_SCALE     },
    { XML_NAMESPACE_CHART_EXT,  XML_DATE_SCALE,         XML_TOK_AXIS_DATE_SCALE_EXT },
    XML_TOKEN_MAP_END
};

class AxisChildTokenMap : public SvXMLTokenMap
{
public:
    AxisChildTokenMap(): SvXMLTokenMap( aAxisChildTokenMap ) {}
    virtual ~AxisChildTokenMap() {}
};

//a AxisChildTokenMap Singleton
struct theAxisChildTokenMap : public rtl::Static< AxisChildTokenMap, theAxisChildTokenMap > {};
}

SvXMLImportContext* SchXMLAxisContext::CreateChildContext(
	sal_uInt16 p_nPrefix,
	const OUString& rLocalName,
	const Reference< xml::sax::XAttributeList >& xAttrList )
{
	SvXMLImportContext* pContext = 0;
    const SvXMLTokenMap& rTokenMap = theAxisChildTokenMap::get();

	switch( rTokenMap.Get( p_nPrefix, rLocalName ))
	{
		case XML_TOK_AXIS_TITLE:
        {
            Reference< drawing::XShape > xTitleShape = getTitleShape();
			pContext = new SchXMLTitleContext( m_rImportHelper, GetImport(), rLocalName,
											   m_aCurrentAxis.aTitle,
											   xTitleShape );
		}
        break;

        case XML_TOK_AXIS_CATEGORIES:
			pContext = new SchXMLCategoriesContext( m_rImportHelper, GetImport(),
														  p_nPrefix, rLocalName,
														  m_rCategoriesAddress );
            m_aCurrentAxis.bHasCategories = true;
			break;

        case XML_TOK_AXIS_DATE_SCALE:
        case XML_TOK_AXIS_DATE_SCALE_EXT:
            pContext = new DateScaleContext( m_rImportHelper, GetImport(),
                            p_nPrefix, rLocalName, m_xAxisProps );
            m_bDateScaleImported = true;
            break;

        case XML_TOK_AXIS_GRID:
		{
			sal_Int16 nAttrCount = xAttrList.is()? xAttrList->getLength(): 0;
			bool bIsMajor = true;		// default value for class is "major"
			OUString sAutoStyleName;

			for( sal_Int16 i = 0; i < nAttrCount; i++ )
			{
				OUString sAttrName = xAttrList->getNameByIndex( i );
				OUString aLocalName;
				sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );

				if( nPrefix == XML_NAMESPACE_CHART )
				{
					if( IsXMLToken( aLocalName, XML_CLASS ) )
					{
						if( IsXMLToken( xAttrList->getValueByIndex( i ), XML_MINOR ) )
							bIsMajor = false;
					}
					else if( IsXMLToken( aLocalName, XML_STYLE_NAME ) )
						sAutoStyleName = xAttrList->getValueByIndex( i );
				}
			}

			CreateGrid( sAutoStyleName, bIsMajor );

			// don't create a context => use default context. grid elements are empty
			pContext = new SvXMLImportContext( GetImport(), p_nPrefix, rLocalName );
		}
        break;

        default:
			pContext = new SvXMLImportContext( GetImport(), p_nPrefix, rLocalName );
            break;
    }

	return pContext;
}

void SchXMLAxisContext::EndElement()
{
    if( !m_bDateScaleImported && m_nAxisType==chart::ChartAxisType::AUTOMATIC )
    {
        Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(), m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) );
        if( xAxis.is() )
        {
            chart2::ScaleData aScaleData( xAxis->getScaleData());
            aScaleData.AutoDateAxis = false;//different default for older documents
            xAxis->setScaleData( aScaleData );
        }
    }

    SetAxisTitle();
}

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

namespace
{

Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem > xCooSys, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
{
    Reference< chart2::XAxis > xAxis;
    try
    {
        xAxis = xCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex );
    }
    catch( uno::Exception & )
    {
    }
    return xAxis;
}

} // anonymous namespace

void SchXMLAxisContext::CorrectAxisPositions( const Reference< chart2::XChartDocument >& xNewDoc,
                          const OUString& rChartTypeServiceName,
                          const OUString& rODFVersionOfFile,
                          bool bAxisPositionAttributeImported )
{
    if( ( !rODFVersionOfFile.getLength() || rODFVersionOfFile.equalsAscii("1.0")
        || rODFVersionOfFile.equalsAscii("1.1")
        || ( rODFVersionOfFile.equalsAscii("1.2") && !bAxisPositionAttributeImported ) ) )
    {
        try
        {
            Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDoc->getFirstDiagram(), uno::UNO_QUERY_THROW );
            uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
            if( aCooSysSeq.getLength() )
            {
                Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] );
                if( xCooSys.is() )
                {
                    Reference< chart2::XAxis > xMainXAxis = lcl_getAxis( xCooSys, 0, 0 );
                    Reference< chart2::XAxis > xMainYAxis = lcl_getAxis( xCooSys, 1, 0 );
                    //Reference< chart2::XAxis > xMajorZAxis = lcl_getAxis( xCooSys, 2, 0 );
                    Reference< chart2::XAxis > xSecondaryXAxis = lcl_getAxis( xCooSys, 0, 1 );
                    Reference< chart2::XAxis > xSecondaryYAxis = lcl_getAxis( xCooSys, 1, 1 );
                    
                    Reference< beans::XPropertySet > xMainXAxisProp( xMainXAxis, uno::UNO_QUERY );
                    Reference< beans::XPropertySet > xMainYAxisProp( xMainYAxis, uno::UNO_QUERY );
                    Reference< beans::XPropertySet > xSecondaryXAxisProp( xSecondaryXAxis, uno::UNO_QUERY );
                    Reference< beans::XPropertySet > xSecondaryYAxisProp( xSecondaryYAxis, uno::UNO_QUERY );
                    
                    if( xMainXAxisProp.is() && xMainYAxisProp.is() )
                    {
                        chart2::ScaleData aMainXScale = xMainXAxis->getScaleData();
                        if( 0 == rChartTypeServiceName.reverseCompareToAsciiL( RTL_CONSTASCII_STRINGPARAM( "com.sun.star.chart2.ScatterChartType" ) ) )
                        {
                            xMainYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_VALUE) );
                            double fCrossoverValue = 0.0;
                            aMainXScale.Origin >>= fCrossoverValue;
                            xMainYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverValue")
                                    , uno::makeAny( fCrossoverValue ) );

                            if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE )
                            {
                                xMainYAxisProp->setPropertyValue( OUString::createFromAscii("LabelPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END) );
                                xMainYAxisProp->setPropertyValue( OUString::createFromAscii("MarkPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS) );
                                if( xSecondaryYAxisProp.is() )
                                    xSecondaryYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_START) );
                            }
                            else
                            {
                                xMainYAxisProp->setPropertyValue( OUString::createFromAscii("LabelPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START) );
                                xMainYAxisProp->setPropertyValue( OUString::createFromAscii("MarkPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS) );
                                if( xSecondaryYAxisProp.is() )
                                    xSecondaryYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_END) );
                            }    
                        }
                        else
                        {
                            if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE )
                            {
                                xMainYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_END) );
                                if( xSecondaryYAxisProp.is() )
                                    xSecondaryYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                        , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_START) );
                            }
                            else
                            {
                                xMainYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                    , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_START) );
                                if( xSecondaryYAxisProp.is() )
                                    xSecondaryYAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                        , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_END) );
                            }
                        }

                        chart2::ScaleData aMainYScale = xMainYAxis->getScaleData();
                        xMainXAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_VALUE) );
                        double fCrossoverValue = 0.0;
                        aMainYScale.Origin >>= fCrossoverValue;
                        xMainXAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverValue")
                                , uno::makeAny( fCrossoverValue ) );

                        if( aMainYScale.Orientation == chart2::AxisOrientation_REVERSE )
                        {
                            xMainXAxisProp->setPropertyValue( OUString::createFromAscii("LabelPosition")
                                , uno::makeAny( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END) );
                            xMainXAxisProp->setPropertyValue( OUString::createFromAscii("MarkPosition")
                                , uno::makeAny( ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS) );
                            if( xSecondaryXAxisProp.is() )
                                xSecondaryXAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_START) );
                        }
                        else
                        {
                            xMainXAxisProp->setPropertyValue( OUString::createFromAscii("LabelPosition")
                                , uno::makeAny( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START) );
                            xMainXAxisProp->setPropertyValue( OUString::createFromAscii("MarkPosition")
                                , uno::makeAny( ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS) );
                            if( xSecondaryXAxisProp.is() )
                                xSecondaryXAxisProp->setPropertyValue( OUString::createFromAscii("CrossoverPosition")
                                , uno::makeAny( ::com::sun::star::chart::ChartAxisPosition_END) );
                        }
                    }
                }
            }
        }
        catch( uno::Exception & )
        {
        }
    }
}

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

SchXMLCategoriesContext::SchXMLCategoriesContext(
	SchXMLImportHelper& rImpHelper,
	SvXMLImport& rImport,
	sal_uInt16 nPrefix,
	const OUString& rLocalName,
	OUString& rAddress ) :
		SvXMLImportContext( rImport, nPrefix, rLocalName ),
		m_rImportHelper( rImpHelper ),
		mrAddress( rAddress )
{
}

SchXMLCategoriesContext::~SchXMLCategoriesContext()
{
}

void SchXMLCategoriesContext::StartElement( const Reference< xml::sax::XAttributeList >& xAttrList )
{
	sal_Int16 nAttrCount = xAttrList.is()? xAttrList->getLength(): 0;

	for( sal_Int16 i = 0; i < nAttrCount; i++ )
	{
		OUString sAttrName = xAttrList->getNameByIndex( i );
		OUString aLocalName;
		sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );

		if( nPrefix == XML_NAMESPACE_TABLE &&
			IsXMLToken( aLocalName, XML_CELL_RANGE_ADDRESS ) )
		{
            Reference< chart2::XChartDocument > xNewDoc( GetImport().GetModel(), uno::UNO_QUERY );
			mrAddress = xAttrList->getValueByIndex( i );
		}
	}
}

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

DateScaleContext::DateScaleContext(
    SchXMLImportHelper& rImpHelper,
    SvXMLImport& rImport,
    sal_uInt16 nPrefix,
    const OUString& rLocalName,
    const Reference< beans::XPropertySet > xAxisProps ) :
        SvXMLImportContext( rImport, nPrefix, rLocalName ),
        m_rImportHelper( rImpHelper ),
        m_xAxisProps( xAxisProps )
{
}

DateScaleContext::~DateScaleContext()
{
}

namespace
{
enum DateScaleAttributeTokens
{
    XML_TOK_DATESCALE_BASE_TIME_UNIT,
    XML_TOK_DATESCALE_MAJOR_INTERVAL_VALUE,
    XML_TOK_DATESCALE_MAJOR_INTERVAL_UNIT,
    XML_TOK_DATESCALE_MINOR_INTERVAL_VALUE,
    XML_TOK_DATESCALE_MINOR_INTERVAL_UNIT
};

SvXMLTokenMapEntry aDateScaleAttributeTokenMap[] =
{
    { XML_NAMESPACE_CHART,  XML_BASE_TIME_UNIT,         XML_TOK_DATESCALE_BASE_TIME_UNIT  },
    { XML_NAMESPACE_CHART,  XML_MAJOR_INTERVAL_VALUE,   XML_TOK_DATESCALE_MAJOR_INTERVAL_VALUE  },
    { XML_NAMESPACE_CHART,  XML_MAJOR_INTERVAL_UNIT,    XML_TOK_DATESCALE_MAJOR_INTERVAL_UNIT  },
    { XML_NAMESPACE_CHART,  XML_MINOR_INTERVAL_VALUE,   XML_TOK_DATESCALE_MINOR_INTERVAL_VALUE  },
    { XML_NAMESPACE_CHART,  XML_MINOR_INTERVAL_UNIT,    XML_TOK_DATESCALE_MINOR_INTERVAL_UNIT  },
    XML_TOKEN_MAP_END
};

class DateScaleAttributeTokenMap : public SvXMLTokenMap
{
public:
    DateScaleAttributeTokenMap(): SvXMLTokenMap( aDateScaleAttributeTokenMap ) {}
    virtual ~DateScaleAttributeTokenMap() {}
};

struct theDateScaleAttributeTokenMap : public rtl::Static< DateScaleAttributeTokenMap, theDateScaleAttributeTokenMap > {};

sal_Int32 lcl_getTimeUnit( const OUString& rValue )
{
    sal_Int32 nTimeUnit = ::com::sun::star::chart::TimeUnit::DAY;
    if( IsXMLToken( rValue, XML_DAYS ) )
        nTimeUnit = ::com::sun::star::chart::TimeUnit::DAY;
    else if( IsXMLToken( rValue, XML_MONTHS ) )
        nTimeUnit = ::com::sun::star::chart::TimeUnit::MONTH;
    else if( IsXMLToken( rValue, XML_YEARS ) )
        nTimeUnit = ::com::sun::star::chart::TimeUnit::YEAR;
    return nTimeUnit;
}

}

void DateScaleContext::StartElement( const Reference< xml::sax::XAttributeList >& xAttrList )
{
    if( !m_xAxisProps.is() )
        return;

    // parse attributes
	sal_Int16 nAttrCount = xAttrList.is()? xAttrList->getLength(): 0;
	const SvXMLTokenMap& rAttrTokenMap = theDateScaleAttributeTokenMap::get();

    bool bSetNewIncrement=false;
    chart::TimeIncrement aIncrement;
    m_xAxisProps->getPropertyValue( OUString::createFromAscii( "TimeIncrement" )) >>= aIncrement;

	for( sal_Int16 i = 0; i < nAttrCount; i++ )
	{
		OUString sAttrName = xAttrList->getNameByIndex( i );
		OUString aLocalName;
		OUString aValue = xAttrList->getValueByIndex( i );
		sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );

		switch( rAttrTokenMap.Get( nPrefix, aLocalName ))
		{
            case XML_TOK_DATESCALE_BASE_TIME_UNIT:
                {
                    aIncrement.TimeResolution = uno::makeAny( lcl_getTimeUnit(aValue) );
                    bSetNewIncrement = true;
                }
                break;
            case XML_TOK_DATESCALE_MAJOR_INTERVAL_VALUE:
                {
                    chart::TimeInterval aInterval(1,0);
                    aIncrement.MajorTimeInterval >>= aInterval;
                    SvXMLUnitConverter::convertNumber( aInterval.Number, aValue );
                    aIncrement.MajorTimeInterval = uno::makeAny(aInterval);
                    bSetNewIncrement = true;
                }
                break;
            case XML_TOK_DATESCALE_MAJOR_INTERVAL_UNIT:
                {
                    chart::TimeInterval aInterval(1,0);
                    aIncrement.MajorTimeInterval >>= aInterval;
                    aInterval.TimeUnit = lcl_getTimeUnit(aValue);
                    aIncrement.MajorTimeInterval = uno::makeAny(aInterval);
                    bSetNewIncrement = true;
                }
                break;
            case XML_TOK_DATESCALE_MINOR_INTERVAL_VALUE:
                {
                    chart::TimeInterval aInterval(1,0);
                    aIncrement.MinorTimeInterval >>= aInterval;
                    SvXMLUnitConverter::convertNumber( aInterval.Number, aValue );
                    aIncrement.MinorTimeInterval = uno::makeAny(aInterval);
                    bSetNewIncrement = true;
                }
                break;
            case XML_TOK_DATESCALE_MINOR_INTERVAL_UNIT:
                {
                    chart::TimeInterval aInterval(1,0);
                    aIncrement.MinorTimeInterval >>= aInterval;
                    aInterval.TimeUnit = lcl_getTimeUnit(aValue);
                    aIncrement.MinorTimeInterval = uno::makeAny(aInterval);
                    bSetNewIncrement = true;
                }
                break;
		}
	}

    if( bSetNewIncrement )
        m_xAxisProps->setPropertyValue( OUString::createFromAscii( "TimeIncrement" ), uno::makeAny( aIncrement ) );
}

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