/* -*- 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 <com/sun/star/animations/XAnimateColor.hpp>
#include <com/sun/star/animations/XCommand.hpp>
#include <com/sun/star/animations/Timing.hpp>
#include <com/sun/star/animations/Event.hpp>
#include <com/sun/star/animations/XAnimateMotion.hpp>
#include <com/sun/star/animations/XAnimatePhysics.hpp>
#include <com/sun/star/animations/XAnimateTransform.hpp>
#include <com/sun/star/animations/XTransitionFilter.hpp>
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/animations/XAudio.hpp>
#include <com/sun/star/animations/AnimationColorSpace.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/AnimationRestart.hpp>
#include <com/sun/star/animations/EventTrigger.hpp>
#include <com/sun/star/animations/AnimationFill.hpp>
#include <com/sun/star/animations/AnimationEndSync.hpp>
#include <com/sun/star/animations/AnimationCalcMode.hpp>
#include <com/sun/star/animations/AnimationAdditiveMode.hpp>
#include <com/sun/star/animations/AnimationTransformType.hpp>
#include <com/sun/star/animations/TransitionType.hpp>
#include <com/sun/star/animations/TransitionSubType.hpp>
#include <com/sun/star/animations/ValuePair.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/presentation/EffectNodeType.hpp>
#include <com/sun/star/presentation/EffectPresetClass.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/presentation/TextAnimationType.hpp>
#include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
#include <com/sun/star/presentation/EffectCommands.hpp>
#include <o3tl/any.hxx>
#include <sax/tools/converter.hxx>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>
 
#include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
#include "sdpropls.hxx"
#include <xmlsdtypes.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmluconv.hxx>
#include <xmloff/xmlexp.hxx>
#include <xmloff/xmlement.hxx>
#include <xmloff/xmlprhdl.hxx>
 
#include <animations.hxx>
#include <xmloff/animationexport.hxx>
 
using namespace css;
using namespace ::cppu;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::presentation;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::beans;
using namespace ::xmloff::token;
 
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::uno::RuntimeException;
using ::com::sun::star::uno::XInterface;
using ::com::sun::star::beans::NamedValue;
using ::com::sun::star::container::XEnumerationAccess;
using ::com::sun::star::container::XEnumeration;
 
namespace xmloff
{
 
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Fill[] =
{
    { XML_DEFAULT,      AnimationFill::DEFAULT },
    { XML_REMOVE,       AnimationFill::REMOVE },
    { XML_FREEZE,       AnimationFill::FREEZE },
    { XML_HOLD,         AnimationFill::HOLD },
    { XML_TRANSITION,   AnimationFill::TRANSITION },
    { XML_AUTO,         AnimationFill::AUTO },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_FillDefault[] =
{
    { XML_INHERIT,      AnimationFill::INHERIT },
    { XML_REMOVE,       AnimationFill::REMOVE },
    { XML_FREEZE,       AnimationFill::FREEZE },
    { XML_HOLD,         AnimationFill::HOLD },
    { XML_TRANSITION,   AnimationFill::TRANSITION },
    { XML_AUTO,         AnimationFill::AUTO },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Restart[] =
{
    { XML_DEFAULT,      AnimationRestart::DEFAULT },
    { XML_ALWAYS,       AnimationRestart::ALWAYS },
    { XML_WHENNOTACTIVE,AnimationRestart::WHEN_NOT_ACTIVE },
    { XML_NEVER,        AnimationRestart::NEVER },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_RestartDefault[] =
{
    { XML_INHERIT,      AnimationRestart::INHERIT },
    { XML_ALWAYS,       AnimationRestart::ALWAYS },
    { XML_WHENNOTACTIVE,AnimationRestart::WHEN_NOT_ACTIVE },
    { XML_NEVER,        AnimationRestart::NEVER },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Endsync[] =
{
    { XML_FIRST,        AnimationEndSync::FIRST },
    { XML_LAST,         AnimationEndSync::LAST },
    { XML_ALL,          AnimationEndSync::ALL },
    { XML_MEDIA,        AnimationEndSync::MEDIA },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_CalcMode[] =
{
    { XML_DISCRETE,     AnimationCalcMode::DISCRETE },
    { XML_LINEAR,       AnimationCalcMode::LINEAR },
    { XML_PACED,        AnimationCalcMode::PACED },
    { XML_SPLINE,       AnimationCalcMode::SPLINE },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_AdditiveMode[] =
{
    { XML_BASE,         AnimationAdditiveMode::BASE },
    { XML_SUM,          AnimationAdditiveMode::SUM },
    { XML_REPLACE,      AnimationAdditiveMode::REPLACE },
    { XML_MULTIPLY,     AnimationAdditiveMode::MULTIPLY },
    { XML_NONE,         AnimationAdditiveMode::NONE },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_TransformType[] =
{
    { XML_TRANSLATE,    AnimationTransformType::TRANSLATE },
    { XML_SCALE,        AnimationTransformType::SCALE },
    { XML_ROTATE,       AnimationTransformType::ROTATE },
    { XML_SKEWX,        AnimationTransformType::SKEWX },
    { XML_SKEWY,        AnimationTransformType::SKEWY },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_TransitionType[] =
{
    { XML_BARWIPE,          TransitionType::BARWIPE },
    { XML_BOXWIPE,          TransitionType::BOXWIPE },
    { XML_FOURBOXWIPE,      TransitionType::FOURBOXWIPE },
    { XML_BARNDOORWIPE,     TransitionType::BARNDOORWIPE },
    { XML_DIAGONALWIPE,     TransitionType::DIAGONALWIPE },
    { XML_BOWTIEWIPE,       TransitionType::BOWTIEWIPE },
    { XML_MISCDIAGONALWIPE, TransitionType::MISCDIAGONALWIPE },
    { XML_VEEWIPE,          TransitionType::VEEWIPE },
    { XML_BARNVEEWIPE,      TransitionType::BARNVEEWIPE },
    { XML_ZIGZAGWIPE,       TransitionType::ZIGZAGWIPE },
    { XML_BARNZIGZAGWIPE,   TransitionType::BARNZIGZAGWIPE },
    { XML_IRISWIPE,         TransitionType::IRISWIPE },
    { XML_TRIANGLEWIPE,     TransitionType::TRIANGLEWIPE },
    { XML_ARROWHEADWIPE,    TransitionType::ARROWHEADWIPE },
    { XML_PENTAGONWIPE,     TransitionType::PENTAGONWIPE },
    { XML_HEXAGONWIPE,      TransitionType::HEXAGONWIPE },
    { XML_ELLIPSEWIPE,      TransitionType::ELLIPSEWIPE },
    { XML_EYEWIPE,          TransitionType::EYEWIPE },
    { XML_ROUNDRECTWIPE,    TransitionType::ROUNDRECTWIPE },
    { XML_STARWIPE,         TransitionType::STARWIPE },
    { XML_MISCSHAPEWIPE,    TransitionType::MISCSHAPEWIPE },
    { XML_CLOCKWIPE,        TransitionType::CLOCKWIPE },
    { XML_PINWHEELWIPE,     TransitionType::PINWHEELWIPE },
    { XML_SINGLESWEEPWIPE,  TransitionType::SINGLESWEEPWIPE },
    { XML_FANWIPE,          TransitionType::FANWIPE },
    { XML_DOUBLEFANWIPE,    TransitionType::DOUBLEFANWIPE },
    { XML_DOUBLESWEEPWIPE,  TransitionType::DOUBLESWEEPWIPE },
    { XML_SALOONDOORWIPE,   TransitionType::SALOONDOORWIPE },
    { XML_WINDSHIELDWIPE,   TransitionType::WINDSHIELDWIPE },
    { XML_SNAKEWIPE,        TransitionType::SNAKEWIPE },
    { XML_SPIRALWIPE,       TransitionType::SPIRALWIPE },
    { XML_PARALLELSNAKESWIPE,TransitionType::PARALLELSNAKESWIPE },
    { XML_BOXSNAKESWIPE,    TransitionType::BOXSNAKESWIPE },
    { XML_WATERFALLWIPE,    TransitionType::WATERFALLWIPE },
    { XML_PUSHWIPE,         TransitionType::PUSHWIPE },
    { XML_SLIDEWIPE,        TransitionType::SLIDEWIPE },
    { XML_FADE,             TransitionType::FADE },
    { XML_RANDOMBARWIPE,    TransitionType::RANDOMBARWIPE },
    { XML_CHECKERBOARDWIPE, TransitionType::CHECKERBOARDWIPE },
    { XML_DISSOLVE,         TransitionType::DISSOLVE },
    { XML_BLINDSWIPE,       TransitionType::BLINDSWIPE },
    { XML_RANDOM,           TransitionType::RANDOM },
    { XML_ZOOM,             TransitionType::ZOOM },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_TransitionSubType[] =
{
    { XML_DEFAULT,              TransitionSubType::DEFAULT },
    { XML_LEFTTORIGHT,          TransitionSubType::LEFTTORIGHT },
    { XML_TOPTOBOTTOM,          TransitionSubType::TOPTOBOTTOM },
    { XML_TOPLEFT,              TransitionSubType::TOPLEFT },
    { XML_TOPRIGHT,             TransitionSubType::TOPRIGHT },
    { XML_BOTTOMRIGHT,          TransitionSubType::BOTTOMRIGHT },
    { XML_BOTTOMLEFT,           TransitionSubType::BOTTOMLEFT },
    { XML_TOPCENTER,            TransitionSubType::TOPCENTER },
    { XML_RIGHTCENTER,          TransitionSubType::RIGHTCENTER },
    { XML_BOTTOMCENTER,         TransitionSubType::BOTTOMCENTER },
    { XML_LEFTCENTER,           TransitionSubType::LEFTCENTER },
    { XML_CORNERSIN,            TransitionSubType::CORNERSIN },
    { XML_CORNERSOUT,           TransitionSubType::CORNERSOUT },
    { XML_VERTICAL,             TransitionSubType::VERTICAL },
    { XML_HORIZONTAL,           TransitionSubType::HORIZONTAL },
    { XML_DIAGONALBOTTOMLEFT,   TransitionSubType::DIAGONALBOTTOMLEFT },
    { XML_DIAGONALTOPLEFT,      TransitionSubType::DIAGONALTOPLEFT },
    { XML_DOUBLEBARNDOOR,       TransitionSubType::DOUBLEBARNDOOR },
    { XML_DOUBLEDIAMOND,        TransitionSubType::DOUBLEDIAMOND },
    { XML_DOWN,                 TransitionSubType::DOWN },
    { XML_LEFT,                 TransitionSubType::LEFT },
    { XML_UP,                   TransitionSubType::UP },
    { XML_RIGHT,                TransitionSubType::RIGHT },
    { XML_RECTANGLE,            TransitionSubType::RECTANGLE },
    { XML_DIAMOND,              TransitionSubType::DIAMOND },
    { XML_CIRCLE,               TransitionSubType::CIRCLE },
    { XML_FOURPOINT,            TransitionSubType::FOURPOINT },
    { XML_FIVEPOINT,            TransitionSubType::FIVEPOINT },
    { XML_SIXPOINT,             TransitionSubType::SIXPOINT },
    { XML_HEART,                TransitionSubType::HEART },
    { XML_KEYHOLE,              TransitionSubType::KEYHOLE },
    { XML_CLOCKWISETWELVE,      TransitionSubType::CLOCKWISETWELVE },
    { XML_CLOCKWISETHREE,       TransitionSubType::CLOCKWISETHREE },
    { XML_CLOCKWISESIX,         TransitionSubType::CLOCKWISESIX },
    { XML_CLOCKWISENINE,        TransitionSubType::CLOCKWISENINE },
    { XML_TWOBLADEVERTICAL,     TransitionSubType::TWOBLADEVERTICAL },
    { XML_TWOBLADEHORIZONTAL,   TransitionSubType::TWOBLADEHORIZONTAL },
    { XML_FOURBLADE,            TransitionSubType::FOURBLADE },
    { XML_CLOCKWISETOP,         TransitionSubType::CLOCKWISETOP },
    { XML_CLOCKWISERIGHT,       TransitionSubType::CLOCKWISERIGHT },
    { XML_CLOCKWISEBOTTOM,      TransitionSubType::CLOCKWISEBOTTOM },
    { XML_CLOCKWISELEFT,        TransitionSubType::CLOCKWISELEFT },
    { XML_CLOCKWISETOPLEFT,     TransitionSubType::CLOCKWISETOPLEFT },
    { XML_COUNTERCLOCKWISEBOTTOMLEFT,TransitionSubType::COUNTERCLOCKWISEBOTTOMLEFT },
    { XML_CLOCKWISEBOTTOMRIGHT, TransitionSubType::CLOCKWISEBOTTOMRIGHT },
    { XML_COUNTERCLOCKWISETOPRIGHT,TransitionSubType::COUNTERCLOCKWISETOPRIGHT },
    { XML_CENTERTOP,            TransitionSubType::CENTERTOP },
    { XML_CENTERRIGHT,          TransitionSubType::CENTERRIGHT },
    { XML_TOP,                  TransitionSubType::TOP },
    { XML_BOTTOM,               TransitionSubType::BOTTOM },
    { XML_FANOUTVERTICAL,       TransitionSubType::FANOUTVERTICAL },
    { XML_FANOUTHORIZONTAL,     TransitionSubType::FANOUTHORIZONTAL },
    { XML_FANINVERTICAL,        TransitionSubType::FANINVERTICAL },
    { XML_FANINHORIZONTAL,      TransitionSubType::FANINHORIZONTAL },
    { XML_PARALLELVERTICAL,     TransitionSubType::PARALLELVERTICAL },
    { XML_PARALLELDIAGONAL,     TransitionSubType::PARALLELDIAGONAL },
    { XML_OPPOSITEVERTICAL,     TransitionSubType::OPPOSITEVERTICAL },
    { XML_OPPOSITEHORIZONTAL,   TransitionSubType::OPPOSITEHORIZONTAL },
    { XML_PARALLELDIAGONALTOPLEFT,TransitionSubType::PARALLELDIAGONALTOPLEFT },
    { XML_PARALLELDIAGONALBOTTOMLEFT,TransitionSubType::PARALLELDIAGONALBOTTOMLEFT },
    { XML_TOPLEFTHORIZONTAL,    TransitionSubType::TOPLEFTHORIZONTAL },
    { XML_TOPLEFTDIAGONAL,      TransitionSubType::TOPLEFTDIAGONAL },
    { XML_TOPRIGHTDIAGONAL,     TransitionSubType::TOPRIGHTDIAGONAL },
    { XML_BOTTOMRIGHTDIAGONAL,  TransitionSubType::BOTTOMRIGHTDIAGONAL },
    { XML_BOTTOMLEFTDIAGONAL,   TransitionSubType::BOTTOMLEFTDIAGONAL },
    { XML_TOPLEFTCLOCKWISE,     TransitionSubType::TOPLEFTCLOCKWISE },
    { XML_TOPRIGHTCLOCKWISE,    TransitionSubType::TOPRIGHTCLOCKWISE },
    { XML_BOTTOMRIGHTCLOCKWISE, TransitionSubType::BOTTOMRIGHTCLOCKWISE },
    { XML_BOTTOMLEFTCLOCKWISE,  TransitionSubType::BOTTOMLEFTCLOCKWISE },
    { XML_TOPLEFTCOUNTERCLOCKWISE,TransitionSubType::TOPLEFTCOUNTERCLOCKWISE },
    { XML_TOPRIGHTCOUNTERCLOCKWISE,TransitionSubType::TOPRIGHTCOUNTERCLOCKWISE },
    { XML_BOTTOMRIGHTCOUNTERCLOCKWISE,TransitionSubType::BOTTOMRIGHTCOUNTERCLOCKWISE },
    { XML_BOTTOMLEFTCOUNTERCLOCKWISE,TransitionSubType::BOTTOMLEFTCOUNTERCLOCKWISE },
    { XML_VERTICALTOPSAME,      TransitionSubType::VERTICALTOPSAME },
    { XML_VERTICALBOTTOMSAME,   TransitionSubType::VERTICALBOTTOMSAME },
    { XML_VERTICALTOPLEFTOPPOSITE,TransitionSubType::VERTICALTOPLEFTOPPOSITE },
    { XML_VERTICALBOTTOMLEFTOPPOSITE,TransitionSubType::VERTICALBOTTOMLEFTOPPOSITE },
    { XML_HORIZONTALLEFTSAME,   TransitionSubType::HORIZONTALLEFTSAME },
    { XML_HORIZONTALRIGHTSAME,  TransitionSubType::HORIZONTALRIGHTSAME },
    { XML_HORIZONTALTOPLEFTOPPOSITE,TransitionSubType::HORIZONTALTOPLEFTOPPOSITE },
    { XML_HORIZONTALTOPRIGHTOPPOSITE,TransitionSubType::HORIZONTALTOPRIGHTOPPOSITE },
    { XML_DIAGONALBOTTOMLEFTOPPOSITE,TransitionSubType::DIAGONALBOTTOMLEFTOPPOSITE },
    { XML_DIAGONALTOPLEFTOPPOSITE,TransitionSubType::DIAGONALTOPLEFTOPPOSITE },
    { XML_TWOBOXTOP,            TransitionSubType::TWOBOXTOP },
    { XML_TWOBOXBOTTOM,         TransitionSubType::TWOBOXBOTTOM },
    { XML_TWOBOXLEFT,           TransitionSubType::TWOBOXLEFT },
    { XML_TWOBOXRIGHT,          TransitionSubType::TWOBOXRIGHT },
    { XML_FOURBOXVERTICAL,      TransitionSubType::FOURBOXVERTICAL },
    { XML_FOURBOXHORIZONTAL,    TransitionSubType::FOURBOXHORIZONTAL },
    { XML_VERTICALLEFT,         TransitionSubType::VERTICALLEFT },
    { XML_VERTICALRIGHT,        TransitionSubType::VERTICALRIGHT },
    { XML_HORIZONTALLEFT,       TransitionSubType::HORIZONTALLEFT },
    { XML_HORIZONTALRIGHT,      TransitionSubType::HORIZONTALRIGHT },
    { XML_FROMLEFT,             TransitionSubType::FROMLEFT },
    { XML_FROMTOP,              TransitionSubType::FROMTOP },
    { XML_FROMRIGHT,            TransitionSubType::FROMRIGHT },
    { XML_FROMBOTTOM,           TransitionSubType::FROMBOTTOM },
    { XML_CROSSFADE,            TransitionSubType::CROSSFADE },
    { XML_FADETOCOLOR,          TransitionSubType::FADETOCOLOR },
    { XML_FADEFROMCOLOR,        TransitionSubType::FADEFROMCOLOR },
    { XML_FADEOVERCOLOR,        TransitionSubType::FADEOVERCOLOR },
    { XML_THREEBLADE,           TransitionSubType::THREEBLADE },
    { XML_EIGHTBLADE,           TransitionSubType::EIGHTBLADE },
    { XML_ONEBLADE,             TransitionSubType::ONEBLADE },
    { XML_ACROSS,               TransitionSubType::ACROSS },
    { XML_TOPLEFTVERTICAL,      TransitionSubType::TOPLEFTVERTICAL },
    { XML_COMBHORIZONTAL,       TransitionSubType::COMBHORIZONTAL },
    { XML_COMBVERTICAL,         TransitionSubType::COMBVERTICAL },
    { XML_IN,                   TransitionSubType::IN },
    { XML_OUT,                  TransitionSubType::OUT },
    { XML_ROTATEIN,             TransitionSubType::ROTATEIN },
    { XML_ROTATEOUT,            TransitionSubType::ROTATEOUT },
    { XML_FROMTOPLEFT,          TransitionSubType::FROMTOPLEFT },
    { XML_FROMTOPRIGHT,         TransitionSubType::FROMTOPRIGHT },
    { XML_FROMBOTTOMLEFT,       TransitionSubType::FROMBOTTOMLEFT },
    { XML_FROMBOTTOMRIGHT,      TransitionSubType::FROMBOTTOMRIGHT },
 
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_EventTrigger[] =
{
    { XML_ONBEGIN,          EventTrigger::ON_BEGIN },
    { XML_ONEND,            EventTrigger::ON_END },
    { XML_BEGIN,            EventTrigger::BEGIN_EVENT },
    { XML_END,              EventTrigger::END_EVENT },
    { XML_CLICK,            EventTrigger::ON_CLICK },
    { XML_DOUBLECLICK,      EventTrigger::ON_DBL_CLICK },
    { XML_MOUSEOVER,        EventTrigger::ON_MOUSE_ENTER },
    { XML_MOUSEOUT,         EventTrigger::ON_MOUSE_LEAVE },
    { XML_NEXT,             EventTrigger::ON_NEXT },
    { XML_PREVIOUS,         EventTrigger::ON_PREV },
    { XML_STOP_AUDIO,       EventTrigger::ON_STOP_AUDIO },
    { XML_REPEAT,           EventTrigger::REPEAT },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_EffectPresetClass[] =
{
    { XML_CUSTOM,       EffectPresetClass::CUSTOM },
    { XML_ENTRANCE,     EffectPresetClass::ENTRANCE },
    { XML_EXIT,         EffectPresetClass::EXIT },
    { XML_EMPHASIS,     EffectPresetClass::EMPHASIS },
    { XML_MOTION_PATH,  EffectPresetClass::MOTIONPATH },
    { XML_OLE_ACTION,   EffectPresetClass::OLEACTION },
    { XML_MEDIA_CALL,   EffectPresetClass::MEDIACALL },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_EffectNodeType[] =
{
    { XML_DEFAULT,                  EffectNodeType::DEFAULT },
    { XML_ON_CLICK,                 EffectNodeType::ON_CLICK },
    { XML_WITH_PREVIOUS,            EffectNodeType::WITH_PREVIOUS },
    { XML_AFTER_PREVIOUS,           EffectNodeType::AFTER_PREVIOUS },
    { XML_MAIN_SEQUENCE,            EffectNodeType::MAIN_SEQUENCE },
    { XML_TIMING_ROOT,              EffectNodeType::TIMING_ROOT },
    { XML_INTERACTIVE_SEQUENCE,     EffectNodeType::INTERACTIVE_SEQUENCE },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_SubItem[] =
{
    { XML_WHOLE,                    ShapeAnimationSubType::AS_WHOLE },
    { XML_BACKGROUND,               ShapeAnimationSubType::ONLY_BACKGROUND },
    { XML_TEXT,                     ShapeAnimationSubType::ONLY_TEXT },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_IterateType[] =
{
    { XML_BY_PARAGRAPH,             TextAnimationType::BY_PARAGRAPH },
    { XML_BY_WORD,                  TextAnimationType::BY_WORD },
    { XML_BY_LETTER,                TextAnimationType::BY_LETTER },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Command[] =
{
    { XML_CUSTOM,                   EffectCommands::CUSTOM },
    { XML_VERB,                     EffectCommands::VERB },
    { XML_PLAY,                     EffectCommands::PLAY },
    { XML_TOGGLE_PAUSE,             EffectCommands::TOGGLEPAUSE },
    { XML_STOP,                     EffectCommands::STOP },
    { XML_STOP_AUDIO,               EffectCommands::STOPAUDIO },
    { XML_TOKEN_INVALID, 0 }
};
 
const struct ImplAttributeNameConversion* getAnimationAttributeNamesConversionList()
{
    static constexpr struct ImplAttributeNameConversion gImplConversionList[]
    {
        { XML_X,                        u"X"_ustr },
        { XML_Y,                        u"Y"_ustr },
        { XML_WIDTH,                    u"Width"_ustr },
        { XML_HEIGHT,                   u"Height"_ustr },
        { XML_ROTATE,                   u"Rotate"_ustr },
        { XML_SKEWX,                    u"SkewX"_ustr },
        { XML_FILL_COLOR,               u"FillColor"_ustr },
        { XML_FILL,                     u"FillStyle"_ustr },
        { XML_STROKE_COLOR,             u"LineColor"_ustr },
        { XML_STROKE,                   u"LineStyle"_ustr },
        { XML_COLOR,                    u"CharColor"_ustr },
        { XML_TEXT_ROTATION_ANGLE,      u"CharRotation"_ustr },
        { XML_FONT_WEIGHT,              u"CharWeight"_ustr },
        { XML_TEXT_UNDERLINE,           u"CharUnderline"_ustr },
        { XML_FONT_FAMILY,              u"CharFontName"_ustr },
        { XML_FONT_SIZE,                u"CharHeight"_ustr },
        { XML_FONT_STYLE,               u"CharPosture"_ustr },
        { XML_VISIBILITY,               u"Visibility"_ustr },
        { XML_OPACITY,                  u"Opacity"_ustr },
        { XML_DIM,                      u"DimColor"_ustr },
        { XML_TOKEN_INVALID,            u""_ustr }
    };
 
    return gImplConversionList;
}
 
 
class AnimationsExporterImpl
{
public:
    AnimationsExporterImpl( SvXMLExport& rExport, const Reference< XPropertySet >& xPageProps );
 
    void prepareNode( const Reference< XAnimationNode >& xNode );
    void exportNode( const Reference< XAnimationNode >& xNode );
 
    void exportContainer( const Reference< XTimeContainer >& xNode, sal_Int16 nContainerNodeType );
    void exportAnimate( const Reference< XAnimate >& xNode );
    void exportAudio( const Reference< XAudio >& xAudio );
    void exportCommand( const Reference< XCommand >& xCommand );
 
    static Reference< XInterface > getParagraphTarget( const ParagraphTarget& pTarget );
 
    static void convertPath( OUStringBuffer& sTmp, const Any& rPath );
    void convertValue( XMLTokenEnum eAttributeName, OUStringBuffer& sTmp, const Any& rValue ) const;
    void convertTiming( OUStringBuffer& sTmp, const Any& rTiming ) const;
    void convertTarget( OUStringBuffer& sTmp, const Any& rTarget ) const;
 
    void prepareValue( const Any& rValue );
 
    void exportTransitionNode();
    void prepareTransitionNode();
 
    bool mbHasTransition;
private:
    rtl::Reference<SvXMLExport> mxExport;
    Reference< XPropertySet > mxPageProps;
    rtl::Reference<XMLSdPropHdlFactory> mxSdPropHdlFactory;
};
 
AnimationsExporterImpl::AnimationsExporterImpl( SvXMLExport& rExport, const Reference< XPropertySet >& xPageProps )
: mbHasTransition(false)
, mxExport( &rExport )
, mxPageProps( xPageProps )
, mxSdPropHdlFactory(new XMLSdPropHdlFactory( rExport.GetModel(), rExport ))
{
}
 
 
/** split a uri hierarchy into first segment and rest */
static bool splitPath(OUString const & i_rPath,
    OUString & o_rDir, OUString& o_rRest)
{
    const sal_Int32 idx(i_rPath.indexOf(u'/'));
    if (idx < 0 || idx >= i_rPath.getLength()) {
        o_rDir = OUString();
        o_rRest = i_rPath;
        return true;
    } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
        // input must not start or end with '/'
        return false;
    } else {
        o_rDir  = i_rPath.copy(0, idx);
        o_rRest = i_rPath.copy(idx+1);
        return true;
    }
}
 
static void lcl_CopyStream(
        uno::Reference<embed::XStorage> const& xSource,
        uno::Reference<embed::XStorage> const& xTarget,
         OUString const& rPath)
{
    OUString dir;
    OUString rest;
    if (!splitPath(rPath, dir, rest))
        throw uno::RuntimeException();
 
    if (dir.getLength() == 0)
        xSource->copyElementTo(rPath, xTarget, rPath);
    else
    {
        uno::Reference<embed::XStorage> const xSubSource(
            xSource->openStorageElement(dir, embed::ElementModes::READ));
        uno::Reference<embed::XStorage> const xSubTarget(
            xTarget->openStorageElement(dir, embed::ElementModes::WRITE));
        lcl_CopyStream(xSubSource, xSubTarget, rest);
    }
    uno::Reference<embed::XTransactedObject> const xTransaction(xTarget, uno::UNO_QUERY);
    if (xTransaction.is())
        xTransaction->commit();
}
 
char const s_PkgScheme[] = "vnd.sun.star.Package:";
 
static OUString lcl_StoreMediaAndGetURL(SvXMLExport & rExport, OUString const& rURL)
{
    if (rURL.startsWithIgnoreAsciiCase(s_PkgScheme))
    {
        try // video is embedded
        {
            // copy the media stream from document storage to target storage
            // (not sure if this is the best way to store these?)
            uno::Reference<document::XStorageBasedDocument> const xSBD(
                    rExport.GetModel(), uno::UNO_QUERY_THROW);
            uno::Reference<embed::XStorage> const xSource(
                    xSBD->getDocumentStorage(), uno::UNO_SET_THROW);
            uno::Reference<embed::XStorage> const xTarget(
                    rExport.GetTargetStorage(), uno::UNO_SET_THROW);
 
            OUString urlPath = rURL.copy(SAL_N_ELEMENTS(s_PkgScheme)-1);
 
            lcl_CopyStream(xSource, xTarget, urlPath);
 
            return urlPath;
        }
        catch (uno::Exception const&)
        {
            TOOLS_INFO_EXCEPTION("xmloff", "exception while storing embedded media");
        }
        return OUString();
    }
    else
    {
        return rExport.GetRelativeReference(rURL); // linked
    }
}
 
void AnimationsExporterImpl::exportTransitionNode()
{
    if( !(mbHasTransition && mxPageProps.is()) )
        return;
 
    sal_Int16 nTransition = 0;
    mxPageProps->getPropertyValue(u"TransitionType"_ustr) >>= nTransition;
 
    Any aSound( mxPageProps->getPropertyValue(u"Sound"_ustr) );
    OUString sSoundURL;
    aSound >>= sSoundURL;
    bool bStopSound = false;
    if( !(aSound >>= bStopSound) )
        bStopSound = false;
 
 
    OUStringBuffer sTmp;
    if( !((nTransition != 0) || !sSoundURL.isEmpty() || bStopSound) )
        return;
 
    Reference< XInterface > xSource( mxPageProps );
    Event aEvent;
    aEvent.Source <<= xSource;
    aEvent.Trigger = EventTrigger::BEGIN_EVENT;
    aEvent.Repeat = 0;
 
    convertTiming( sTmp, Any( aEvent ) );
    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_BEGIN, sTmp.makeStringAndClear() );
 
    SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, XML_PAR, true, true );
 
    if( nTransition != 0 )
    {
        sal_Int16 nSubtype = 0;
        bool bDirection = false;
        sal_Int32 nFadeColor = 0;
        double fDuration = 0.0;
        mxPageProps->getPropertyValue(u"TransitionSubtype"_ustr) >>= nSubtype;
        mxPageProps->getPropertyValue(u"TransitionDirection"_ustr) >>= bDirection;
        mxPageProps->getPropertyValue(u"TransitionFadeColor"_ustr) >>= nFadeColor;
        mxPageProps->getPropertyValue(u"TransitionDuration"_ustr) >>= fDuration;
 
        ::sax::Converter::convertDouble( sTmp, fDuration );
        sTmp.append( 's');
        mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DUR, sTmp.makeStringAndClear() );
 
        SvXMLUnitConverter::convertEnum( sTmp, nTransition, aAnimations_EnumMap_TransitionType );
        mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TYPE, sTmp.makeStringAndClear() );
 
        if( nSubtype != TransitionSubType::DEFAULT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nSubtype, aAnimations_EnumMap_TransitionSubType );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_SUBTYPE, sTmp.makeStringAndClear() );
        }
 
        if( !bDirection )
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DIRECTION, XML_REVERSE );
 
        if( (nTransition == TransitionType::FADE)
                && ((nSubtype == TransitionSubType::FADETOCOLOR) || (nSubtype == TransitionSubType::FADEFROMCOLOR)
                    || (nSubtype == TransitionSubType::FADEOVERCOLOR)))
        {
            ::sax::Converter::convertColor( sTmp, nFadeColor );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FADECOLOR, sTmp.makeStringAndClear() );
        }
        SvXMLElementExport aElement2( *mxExport, XML_NAMESPACE_ANIMATION, XML_TRANSITIONFILTER, true, true );
    }
 
    if( bStopSound )
    {
        mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COMMAND, XML_STOP_AUDIO );
        SvXMLElementExport aElement2( *mxExport, XML_NAMESPACE_ANIMATION, XML_COMMAND, true, true );
    }
    else if( !sSoundURL.isEmpty())
    {
        sSoundURL = lcl_StoreMediaAndGetURL(*mxExport, sSoundURL);
        mxExport->AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sSoundURL );
 
        bool bLoopSound = false;
        mxPageProps->getPropertyValue(u"LoopSound"_ustr) >>= bLoopSound;
 
        if( bLoopSound )
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATCOUNT, XML_INDEFINITE );
        SvXMLElementExport aElement2( *mxExport, XML_NAMESPACE_ANIMATION, XML_AUDIO, true, true );
    }
}
 
void AnimationsExporterImpl::prepareTransitionNode()
{
    if( !mxPageProps.is() )
        return;
 
    try
    {
        sal_Int16 nTransition = 0;
        mxPageProps->getPropertyValue(u"TransitionType"_ustr) >>= nTransition;
 
        bool bStopSound = false;
        OUString sSoundURL;
 
        if( nTransition == 0 )
        {
            Any aSound( mxPageProps->getPropertyValue(u"Sound"_ustr) );
            aSound >>= sSoundURL;
 
            if( !(aSound >>= bStopSound) )
                bStopSound = false;
        }
 
        if( (nTransition != 0) || !sSoundURL.isEmpty() || bStopSound )
        {
            mbHasTransition = true;
            Reference< XInterface > xInt( mxPageProps );
            mxExport->getInterfaceToIdentifierMapper().registerReference( xInt );
        }
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
void AnimationsExporterImpl::prepareNode( const Reference< XAnimationNode >& xNode )
{
    try
    {
        prepareValue( xNode->getBegin() );
        prepareValue( xNode->getEnd() );
 
        sal_Int16 nNodeType = xNode->getType();
        switch( nNodeType )
        {
        case AnimationNodeType::ITERATE:
        {
            Reference< XIterateContainer > xIter( xNode, UNO_QUERY_THROW );
            prepareValue( xIter->getTarget() );
            [[fallthrough]];
        }
        case AnimationNodeType::PAR:
        case AnimationNodeType::SEQ:
        {
            Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
            Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );
            while( xEnumeration->hasMoreElements() )
            {
                Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
                prepareNode( xChildNode );
            }
        }
        break;
 
        case AnimationNodeType::ANIMATE:
        case AnimationNodeType::SET:
        case AnimationNodeType::ANIMATEMOTION:
        case AnimationNodeType::ANIMATEPHYSICS:
        case AnimationNodeType::ANIMATECOLOR:
        case AnimationNodeType::ANIMATETRANSFORM:
        case AnimationNodeType::TRANSITIONFILTER:
        {
            Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW );
            prepareValue( xAnimate->getTarget() );
        }
        break;
 
        case AnimationNodeType::COMMAND:
        {
            Reference< XCommand > xCommand( xNode, UNO_QUERY_THROW );
            prepareValue( xCommand->getTarget() );
        }
        break;
 
        case AnimationNodeType::AUDIO:
        {
            Reference< XAudio > xAudio( xNode, UNO_QUERY_THROW );
            prepareValue( xAudio->getSource() );
        }
        break;
        }
 
        const Sequence< NamedValue > aUserData( xNode->getUserData() );
        for( const auto& rValue : aUserData )
        {
            if( IsXMLToken( rValue.Name, XML_MASTER_ELEMENT ) )
            {
                Reference< XInterface > xMaster;
                rValue.Value >>= xMaster;
                if( xMaster.is() )
                    mxExport->getInterfaceToIdentifierMapper().registerReference( xMaster );
            }
        }
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
void AnimationsExporterImpl::exportNode( const Reference< XAnimationNode >& xNode )
{
    try
    {
        OUStringBuffer sTmp;
 
        const OUString& rExportIdentifier = mxExport->getInterfaceToIdentifierMapper().getIdentifier( xNode );
        if( !rExportIdentifier.isEmpty() )
        {
            mxExport->AddAttributeIdLegacy(
                XML_NAMESPACE_ANIMATION, rExportIdentifier);
        }
 
        Any aTemp( xNode->getBegin() );
        if( aTemp.hasValue() )
        {
            convertTiming( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_BEGIN, sTmp.makeStringAndClear() );
        }
 
        double fTemp = 0;
        sal_Int16 nTemp;
 
        aTemp = xNode->getDuration();
        if( aTemp.hasValue() )
        {
            if( aTemp >>= fTemp )
            {
                ::sax::Converter::convertDouble( sTmp, fTemp );
                sTmp.append( 's');
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DUR, sTmp.makeStringAndClear() );
            }
            else
            {
                Timing eTiming;
                if( aTemp >>= eTiming )
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DUR, eTiming == Timing_INDEFINITE ? XML_INDEFINITE : XML_MEDIA );
            }
        }
 
        aTemp = xNode->getEnd();
        if( aTemp.hasValue() )
        {
            convertTiming( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_END, sTmp.makeStringAndClear() );
        }
 
        nTemp = xNode->getFill();
        if( nTemp != AnimationFill::DEFAULT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_Fill );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FILL, sTmp.makeStringAndClear() );
        }
 
        nTemp = xNode->getFillDefault();
        if( nTemp != AnimationFill::INHERIT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_FillDefault );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FILLDEFAULT, sTmp.makeStringAndClear() );
        }
 
        nTemp = xNode->getRestart();
        if( nTemp != AnimationRestart::DEFAULT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_Restart );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_RESTART, sTmp.makeStringAndClear() );
        }
 
        nTemp = xNode->getRestartDefault();
        if( nTemp != AnimationRestart::INHERIT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_RestartDefault );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_RESTARTDEFAULT, sTmp.makeStringAndClear() );
        }
 
        fTemp = xNode->getAcceleration();
        if( fTemp != 0.0 )
        {
            ::sax::Converter::convertDouble( sTmp, fTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ACCELERATE, sTmp.makeStringAndClear() );
        }
 
        fTemp = xNode->getDecelerate();
        if( fTemp != 0.0 )
        {
            ::sax::Converter::convertDouble( sTmp, fTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DECELERATE, sTmp.makeStringAndClear() );
        }
 
        bool bTemp = xNode->getAutoReverse();
        if( bTemp )
        {
            ::sax::Converter::convertBool( sTmp, bTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_AUTOREVERSE, sTmp.makeStringAndClear() );
        }
 
        aTemp = xNode->getRepeatCount();
        if( aTemp.hasValue() )
        {
            Timing eTiming;
            if( (aTemp >>= eTiming ) && (eTiming == Timing_INDEFINITE ) )
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATCOUNT, XML_INDEFINITE );
            else if( aTemp >>= fTemp )
            {
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATCOUNT, sTmp.makeStringAndClear() );
            }
        }
 
        aTemp = xNode->getRepeatDuration();
        if( aTemp.hasValue() )
        {
            Timing eTiming;
            if( ( aTemp >>= eTiming ) && (eTiming == Timing_INDEFINITE) )
            {
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATDUR, XML_INDEFINITE );
            }
            else if( aTemp >>= fTemp )
            {
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATDUR, sTmp.makeStringAndClear() );
            }
        }
 
        aTemp = xNode->getEndSync();
        if( aTemp.hasValue() && (aTemp >>= nTemp) )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_Endsync );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ENDSYNC, sTmp.makeStringAndClear() );
        }
 
        sal_Int16 nContainerNodeType = EffectNodeType::DEFAULT;
        OUString aPresetId;
        const Sequence< NamedValue > aUserData( xNode->getUserData() );
        for( const auto& rValue : aUserData )
        {
            if( IsXMLToken( rValue.Name, XML_NODE_TYPE ) )
            {
                if( (rValue.Value >>= nContainerNodeType) && (nContainerNodeType != EffectNodeType::DEFAULT) )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nContainerNodeType, aAnimations_EnumMap_EffectNodeType );
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_NODE_TYPE, sTmp.makeStringAndClear() );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_PRESET_ID ) )
            {
                if( rValue.Value >>= aPresetId )
                {
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_PRESET_ID, aPresetId );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_PRESET_SUB_TYPE ) )
            {
                OUString aPresetSubType;
                if( rValue.Value >>= aPresetSubType )
                {
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_PRESET_SUB_TYPE, aPresetSubType );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_PRESET_CLASS ) )
            {
                sal_Int16 nEffectPresetClass = sal_uInt16();
                if( rValue.Value >>= nEffectPresetClass )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nEffectPresetClass, aAnimations_EnumMap_EffectPresetClass );
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_PRESET_CLASS, sTmp.makeStringAndClear() );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_MASTER_ELEMENT ) )
            {
                Reference< XInterface > xMaster;
                rValue.Value >>= xMaster;
                if( xMaster.is() )
                {
                    const OUString& rIdentifier = mxExport->getInterfaceToIdentifierMapper().getIdentifier(xMaster);
                    if( !rIdentifier.isEmpty() )
                        mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_MASTER_ELEMENT, rIdentifier );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_GROUP_ID ) )
            {
                sal_Int32 nGroupId = 0;
                if( rValue.Value >>= nGroupId )
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_GROUP_ID, OUString::number( nGroupId ) );
            }
            else
            {
                OUString aTmp;
                if( rValue.Value >>= aTmp )
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, rValue.Name, aTmp );
            }
        }
 
        nTemp = xNode->getType();
        switch( nTemp )
        {
        case AnimationNodeType::PAR:
        case AnimationNodeType::SEQ:
        case AnimationNodeType::ITERATE:
        {
            Reference< XTimeContainer > xContainer( xNode, UNO_QUERY_THROW );
            exportContainer( xContainer, nContainerNodeType );
        }
        break;
 
        case AnimationNodeType::ANIMATE:
        case AnimationNodeType::SET:
        case AnimationNodeType::ANIMATEMOTION:
        case AnimationNodeType::ANIMATEPHYSICS:
        case AnimationNodeType::ANIMATECOLOR:
        case AnimationNodeType::ANIMATETRANSFORM:
        case AnimationNodeType::TRANSITIONFILTER:
        {
            Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW );
            exportAnimate( xAnimate );
        }
        break;
        case AnimationNodeType::AUDIO:
        {
            Reference< XAudio > xAudio( xNode, UNO_QUERY_THROW );
            exportAudio( xAudio );
        }
        break;
        case AnimationNodeType::COMMAND:
        {
            Reference< XCommand > xCommand( xNode, UNO_QUERY_THROW );
            exportCommand( xCommand );
        }
        break;
        default:
            OSL_FAIL( "xmloff::AnimationsExporterImpl::exportNode(), invalid AnimationNodeType!" );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
 
    // if something goes wrong, its always a good idea to clear the attribute list
    mxExport->ClearAttrList();
}
 
void AnimationsExporterImpl::exportContainer( const Reference< XTimeContainer >& xContainer, sal_Int16 nContainerNodeType )
{
    try
    {
        const sal_Int32 nNodeType = xContainer->getType();
 
        if( nNodeType == AnimationNodeType::ITERATE )
        {
            OUStringBuffer sTmp;
            Reference< XIterateContainer > xIter( xContainer, UNO_QUERY_THROW );
 
            Any aTemp( xIter->getTarget() );
            if( aTemp.hasValue() )
            {
                convertTarget( sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TARGETELEMENT, sTmp.makeStringAndClear() );
            }
 
            sal_Int16 nTemp = xIter->getSubItem();
            if( nTemp )
            {
                SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_SubItem );
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_SUB_ITEM, sTmp.makeStringAndClear() );
            }
 
            nTemp = xIter->getIterateType();
            if( nTemp )
            {
                SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_IterateType );
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_ITERATE_TYPE, sTmp.makeStringAndClear() );
            }
 
            double fTemp = xIter->getIterateInterval();
            if( fTemp )
            {
                OUStringBuffer buf;
                ::sax::Converter::convertDuration(buf, fTemp / (24*60*60));
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION,
                        XML_ITERATE_INTERVAL, buf.makeStringAndClear());
            }
        }
 
        XMLTokenEnum eElementToken;
        switch( nNodeType )
        {
        case AnimationNodeType::PAR:    eElementToken = XML_PAR; break;
        case AnimationNodeType::SEQ:    eElementToken = XML_SEQ; break;
        case AnimationNodeType::ITERATE:eElementToken = XML_ITERATE; break;
        default:
            OSL_FAIL( "xmloff::AnimationsExporterImpl::exportContainer(), invalid TimeContainerType!" );
            return;
        }
        SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, eElementToken, true, true );
 
        if( nContainerNodeType == EffectNodeType::TIMING_ROOT )
            exportTransitionNode();
 
        Reference< XEnumerationAccess > xEnumerationAccess( xContainer, UNO_QUERY_THROW );
        Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );
        while( xEnumeration->hasMoreElements() )
        {
            Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
            exportNode( xChildNode );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
void AnimationsExporterImpl::exportAnimate( const Reference< XAnimate >& xAnimate )
{
    try
    {
        const sal_Int16 nNodeType = xAnimate->getType();
 
        OUStringBuffer sTmp;
        sal_Int16 nTemp;
        bool bTemp;
 
        Any aTemp( xAnimate->getTarget() );
        if( aTemp.hasValue() )
        {
            convertTarget( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TARGETELEMENT, sTmp.makeStringAndClear() );
        }
 
        nTemp = xAnimate->getSubItem();
        if( nTemp )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_SubItem );
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_SUB_ITEM, sTmp.makeStringAndClear() );
        }
 
        XMLTokenEnum eAttributeName = XML_TOKEN_INVALID;
 
        if( nNodeType == AnimationNodeType::TRANSITIONFILTER )
        {
            eAttributeName = XML_TRANSITIONFILTER;
        }
        else if( nNodeType == AnimationNodeType::ANIMATETRANSFORM )
        {
            eAttributeName = XML_ANIMATETRANSFORM;
        }
        else if( nNodeType == AnimationNodeType::ANIMATEMOTION )
        {
            eAttributeName = XML_ANIMATEMOTION;
        }
        else if( nNodeType == AnimationNodeType::ANIMATEPHYSICS )
        {
            eAttributeName = XML_ANIMATEPHYSICS;
        }
        else
        {
            OUString sTemp( xAnimate->getAttributeName() );
            if( !sTemp.isEmpty() )
            {
                const struct ImplAttributeNameConversion* p = getAnimationAttributeNamesConversionList();
                while( !p->maAPIName.isEmpty() )
                {
                    if( sTemp == p->maAPIName )
                    {
                        sTemp = GetXMLToken( p->meXMLToken );
                        eAttributeName = p->meXMLToken;
                        break;
                    }
 
                    p++;
                }
 
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ATTRIBUTENAME, sTemp );
            }
            else
            {
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ATTRIBUTENAME, u"invalid"_ustr );
            }
        }
 
        Sequence< Any > aValues( xAnimate->getValues() );
        if( aValues.hasElements() )
        {
            aTemp <<= aValues;
            convertValue( eAttributeName, sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_VALUES, sTmp.makeStringAndClear() );
        }
        else
        {
            aTemp = xAnimate->getFrom();
            if( aTemp.hasValue() )
            {
                convertValue( eAttributeName, sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FROM, sTmp.makeStringAndClear() );
            }
 
            aTemp = xAnimate->getBy();
            if( aTemp.hasValue() )
            {
                convertValue( eAttributeName, sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_BY, sTmp.makeStringAndClear() );
            }
 
            aTemp = xAnimate->getTo();
            if( aTemp.hasValue() )
            {
                convertValue( eAttributeName, sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TO, sTmp.makeStringAndClear() );
            }
        }
 
        if(nNodeType != AnimationNodeType::SET)
        {
            const Sequence< double > aKeyTimes( xAnimate->getKeyTimes() );
            if( aKeyTimes.hasElements() )
            {
                for( const auto& rKeyTime : aKeyTimes )
                {
                    if( !sTmp.isEmpty() )
                        sTmp.append( ';' );
 
                    sTmp.append( rKeyTime );
                }
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_KEYTIMES, sTmp.makeStringAndClear() );
            }
 
            OUString sTemp( xAnimate->getFormula() );
            if( !sTemp.isEmpty() )
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_FORMULA, sTemp );
 
            if( (nNodeType != AnimationNodeType::TRANSITIONFILTER) &&
                (nNodeType != AnimationNodeType::AUDIO ) )
            {
                // calcMode  = "discrete | linear | paced | spline"
                nTemp = xAnimate->getCalcMode();
                if( ((nNodeType == AnimationNodeType::ANIMATEMOTION ) && (nTemp != AnimationCalcMode::PACED)) ||
                    ((nNodeType != AnimationNodeType::ANIMATEMOTION ) && (nTemp != AnimationCalcMode::LINEAR)) )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_CalcMode );
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_CALCMODE, sTmp.makeStringAndClear() );
                }
 
                bTemp = xAnimate->getAccumulate();
                if( bTemp )
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ACCUMULATE, XML_SUM );
 
                nTemp = xAnimate->getAdditive();
                if( nTemp != AnimationAdditiveMode::REPLACE )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_AdditiveMode );
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ADDITIVE, sTmp.makeStringAndClear() );
                }
            }
 
            const Sequence< TimeFilterPair > aTimeFilter( xAnimate->getTimeFilter() );
            if( aTimeFilter.hasElements() )
            {
                for( const auto& rPair : aTimeFilter )
                {
                    if( !sTmp.isEmpty() )
                        sTmp.append( ';' );
 
                    sTmp.append(OUString::number(rPair.Time) + "," + OUString::number(rPair.Progress));
                }
 
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_KEYSPLINES, sTmp.makeStringAndClear() );
            }
        }
 
        XMLTokenEnum eElementToken = XML_ANIMATE;
 
        switch( nNodeType )
        {
        case AnimationNodeType::ANIMATE:
            eElementToken = XML_ANIMATE;
            break;
 
        case AnimationNodeType::SET:
            eElementToken = XML_SET;
            break;
 
        case AnimationNodeType::ANIMATEMOTION:
        {
            eElementToken = XML_ANIMATEMOTION;
 
            Reference< XAnimateMotion > xAnimateMotion( xAnimate, UNO_QUERY_THROW );
 
            aTemp = xAnimateMotion->getPath();
            if( aTemp.hasValue() )
            {
                convertPath( sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SVG, XML_PATH, sTmp.makeStringAndClear() );
            }
 
            // TODO: origin = ( parent | layout )
            aTemp = xAnimateMotion->getOrigin();
        }
        break;
 
        case AnimationNodeType::ANIMATEPHYSICS:
        {
            eElementToken = XML_ANIMATEPHYSICS;
            double fTemp = 0;
 
            Reference< XAnimatePhysics > xAnimatePhysics( xAnimate, UNO_QUERY_THROW );
            aTemp = xAnimatePhysics->getStartVelocityX();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_START_VELOCITY_X, sTmp.makeStringAndClear() );
            }
 
            aTemp = xAnimatePhysics->getStartVelocityY();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_START_VELOCITY_Y, sTmp.makeStringAndClear() );
            }
 
            aTemp = xAnimatePhysics->getDensity();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_DENSITY, sTmp.makeStringAndClear() );
            }
 
            aTemp = xAnimatePhysics->getBounciness();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_BOUNCINESS, sTmp.makeStringAndClear() );
            }
        }
        break;
 
        case AnimationNodeType::ANIMATECOLOR:
        {
            eElementToken = XML_ANIMATECOLOR;
 
            Reference< XAnimateColor > xAnimateColor( xAnimate, UNO_QUERY_THROW );
 
            nTemp = xAnimateColor->getColorInterpolation();
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COLOR_INTERPOLATION, (nTemp == AnimationColorSpace::RGB) ? XML_RGB : XML_HSL );
 
            bTemp = xAnimateColor->getDirection();
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COLOR_INTERPOLATION_DIRECTION, bTemp ? XML_CLOCKWISE : XML_COUNTER_CLOCKWISE );
        }
        break;
 
        case AnimationNodeType::ANIMATETRANSFORM:
        {
            eElementToken = XML_ANIMATETRANSFORM;
 
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ATTRIBUTENAME, XML_TRANSFORM );
 
            Reference< XAnimateTransform > xTransform( xAnimate, UNO_QUERY_THROW );
            nTemp = xTransform->getTransformType();
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_TransformType );
            mxExport->AddAttribute( XML_NAMESPACE_SVG, XML_TYPE, sTmp.makeStringAndClear() );
        }
        break;
 
        case AnimationNodeType::TRANSITIONFILTER:
        {
            Reference< XTransitionFilter > xTransitionFilter( xAnimate, UNO_QUERY );
            eElementToken = XML_TRANSITIONFILTER;
 
            sal_Int16 nTransition = xTransitionFilter->getTransition();
            SvXMLUnitConverter::convertEnum( sTmp, nTransition, aAnimations_EnumMap_TransitionType );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TYPE, sTmp.makeStringAndClear() );
 
            sal_Int16 nSubtype = xTransitionFilter->getSubtype();
            if( nSubtype != TransitionSubType::DEFAULT )
            {
                SvXMLUnitConverter::convertEnum( sTmp, nSubtype, aAnimations_EnumMap_TransitionSubType );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_SUBTYPE, sTmp.makeStringAndClear() );
            }
 
            bTemp = xTransitionFilter->getMode();
            if( !bTemp )
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_MODE, XML_OUT );
 
            bTemp = xTransitionFilter->getDirection();
            if( !bTemp )
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DIRECTION, XML_REVERSE );
 
            if( (nTransition == TransitionType::FADE) && ((nSubtype == TransitionSubType::FADETOCOLOR) || (nSubtype == TransitionSubType::FADEFROMCOLOR) ))
            {
                nTemp = xTransitionFilter->getFadeColor();
                ::sax::Converter::convertColor( sTmp, nTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FADECOLOR, sTmp.makeStringAndClear() );
            }
        }
        break;
        }
 
        if( eElementToken == XML_ANIMATEPHYSICS ) // not a standard should use the extension namespace
        {
            SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_LO_EXT, eElementToken, true, true );
        }
        else
        {
            SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, eElementToken, true, true );
        }
 
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
void AnimationsExporterImpl::exportAudio( const Reference< XAudio >& xAudio )
{
    if( !xAudio.is() )
        return;
 
    try
    {
        OUString aSourceURL;
        xAudio->getSource() >>= aSourceURL;
        if( !aSourceURL.isEmpty() )
            mxExport->AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, mxExport->GetRelativeReference( aSourceURL ) );
 
        const double fVolume = xAudio->getVolume();
        if( fVolume != 1.0 )
        {
            OUStringBuffer sTmp;
            ::sax::Converter::convertDouble( sTmp, fVolume );
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_AUDIO_LEVEL, sTmp.makeStringAndClear() );
        }
 
    /* todo?
       sal_Int32 nEndAfterSlide = 0;
        xAudio->getEndAfterSlide() >>= nEndAfterSlide;
        if( nEndAfterSlide != 0 )
            mxExport->AddAttribute( );
    */
        SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, XML_AUDIO, true, true );
 
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
void AnimationsExporterImpl::exportCommand( const Reference< XCommand >& xCommand )
{
    if( !xCommand.is() )
        return;
 
    try
    {
        OUStringBuffer sTmp;
        Any aTemp( xCommand->getTarget() );
        if( aTemp.hasValue() )
        {
            convertTarget( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TARGETELEMENT, sTmp.makeStringAndClear() );
        }
 
        sal_Int16 nCommand = xCommand->getCommand();
        SvXMLUnitConverter::convertEnum( sTmp, nCommand, aAnimations_EnumMap_Command );
        mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COMMAND, sTmp.makeStringAndClear() );
 
    // todo virtual css::uno::Any SAL_CALL getParameter() throw (css::uno::RuntimeException) = 0;
 
        SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, XML_COMMAND, true, true );
 
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
Reference< XInterface > AnimationsExporterImpl::getParagraphTarget( const ParagraphTarget& pTarget )
{
    try
    {
        Reference< XEnumerationAccess > xParaEnumAccess( pTarget.Shape, UNO_QUERY_THROW );
 
        Reference< XEnumeration > xEnumeration( xParaEnumAccess->createEnumeration(), css::uno::UNO_SET_THROW );
        sal_Int32 nParagraph = pTarget.Paragraph;
 
        while( xEnumeration->hasMoreElements() )
        {
            Reference< XInterface > xRef( xEnumeration->nextElement(), UNO_QUERY );
            if( nParagraph-- == 0 )
                return xRef;
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
 
    Reference< XInterface > xRef;
    return xRef;
}
 
void AnimationsExporterImpl::convertPath( OUStringBuffer& sTmp, const Any& rPath )
{
    OUString aStr;
    rPath >>= aStr;
 
    sTmp = aStr;
}
 
void AnimationsExporterImpl::convertValue( XMLTokenEnum eAttributeName, OUStringBuffer& sTmp, const Any& rValue ) const
{
    if( !rValue.hasValue() )
        return;
 
    if( auto pValuePair = o3tl::tryAccess<ValuePair>(rValue) )
    {
        OUStringBuffer sTmp2;
        convertValue( eAttributeName, sTmp, pValuePair->First );
        sTmp.append( ',' );
        convertValue( eAttributeName, sTmp2, pValuePair->Second );
        sTmp.append( sTmp2 );
    }
    else if( auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue) )
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();
 
        OUStringBuffer sTmp2;
 
        for( nElement = 0; nElement < nLength; nElement++, pAny++ )
        {
            if( !sTmp.isEmpty() )
                sTmp.append( ';' );
            convertValue( eAttributeName, sTmp2, *pAny );
            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }
    }
    else
    {
        sal_Int32 nType;
 
        switch( eAttributeName )
        {
        case XML_X:
        case XML_Y:
        case XML_WIDTH:
        case XML_HEIGHT:
        case XML_ANIMATETRANSFORM:
        case XML_ANIMATEMOTION:
        case XML_ANIMATEPHYSICS:
        {
            if( auto aString = o3tl::tryAccess<OUString>(rValue) )
            {
                sTmp.append( *aString );
            }
            else if( auto x = o3tl::tryAccess<double>(rValue) )
            {
                sTmp.append( *x );
            }
            else
            {
                OSL_FAIL( "xmloff::AnimationsExporterImpl::convertValue(), invalid value type!" );
            }
            return;
        }
 
        case XML_SKEWX:
        case XML_ROTATE:            nType = XML_TYPE_DOUBLE;                    break;
        case XML_TEXT_ROTATION_ANGLE: nType = XML_TYPE_NUMBER16;                break;
        case XML_FILL_COLOR:
        case XML_STROKE_COLOR:
        case XML_DIM:
        case XML_COLOR:             nType = XML_TYPE_COLOR;                     break;
        case XML_FILL:              nType = XML_SD_TYPE_FILLSTYLE;              break;
        case XML_STROKE:            nType = XML_SD_TYPE_STROKE;                 break;
        case XML_FONT_WEIGHT:       nType = XML_TYPE_TEXT_WEIGHT;               break;
        case XML_FONT_STYLE:        nType = XML_TYPE_TEXT_POSTURE;              break;
        case XML_TEXT_UNDERLINE:    nType = XML_TYPE_TEXT_UNDERLINE_STYLE;      break;
        case XML_FONT_SIZE:         nType = XML_TYPE_DOUBLE_PERCENT;            break;
        case XML_VISIBILITY:        nType = XML_SD_TYPE_PRESPAGE_VISIBILITY;    break;
        case XML_OPACITY:
        case XML_TRANSITIONFILTER:  nType = XML_TYPE_DOUBLE;                    break;
        default:
            OSL_FAIL( "xmloff::AnimationsExporterImpl::convertValue(), invalid AttributeName!" );
            nType = XML_TYPE_STRING;
        }
 
        //const XMLPropertyHandler* pHandler = static_cast<SdXMLExport*>(&mrExport)->GetSdPropHdlFactory()->GetPropertyHandler( nType );
        const XMLPropertyHandler* pHandler = mxSdPropHdlFactory->GetPropertyHandler( nType );
        if( pHandler )
        {
            OUString aString;
            pHandler->exportXML( aString, rValue, mxExport->GetMM100UnitConverter() );
            sTmp.append( aString );
        }
    }
}
 
void AnimationsExporterImpl::convertTiming( OUStringBuffer& sTmp, const Any& rValue ) const
{
    if( !rValue.hasValue() )
        return;
 
    if( auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue) )
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();
 
        OUStringBuffer sTmp2;
 
        for( nElement = 0; nElement < nLength; nElement++, pAny++ )
        {
            if( !sTmp.isEmpty() )
                sTmp.append( ';' );
            convertTiming( sTmp2, *pAny );
            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }
    }
    else if( auto x = o3tl::tryAccess<double>(rValue) )
    {
        sTmp.append( *x );
        sTmp.append( 's');
    }
    else if( auto pTiming = o3tl::tryAccess<Timing>(rValue) )
    {
        sTmp.append( GetXMLToken( (*pTiming == Timing_MEDIA) ? XML_MEDIA : XML_INDEFINITE ) );
    }
    else if( auto pEvent = o3tl::tryAccess<Event>(rValue) )
    {
        OUStringBuffer sTmp2;
 
        if( pEvent->Trigger != EventTrigger::NONE )
        {
            if( pEvent->Source.hasValue() )
            {
                convertTarget( sTmp, pEvent->Source );
                sTmp.append( '.' );
            }
 
            SvXMLUnitConverter::convertEnum( sTmp2, pEvent->Trigger, aAnimations_EnumMap_EventTrigger );
 
            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }
 
        if( pEvent->Offset.hasValue() )
        {
            convertTiming( sTmp2, pEvent->Offset );
 
            if( !sTmp.isEmpty() )
                sTmp.append( '+' );
 
            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }
    }
    else
    {
        OSL_FAIL( "xmloff::AnimationsExporterImpl::convertTiming(), invalid value type!" );
    }
}
 
void AnimationsExporterImpl::convertTarget( OUStringBuffer& sTmp, const Any& rTarget ) const
{
    if( !rTarget.hasValue() )
        return;
 
    Reference< XInterface > xRef;
 
    if( !(rTarget >>= xRef) )
    {
        if( auto pt = o3tl::tryAccess<ParagraphTarget>(rTarget) )
        {
            xRef = getParagraphTarget( *pt );
        }
    }
 
    SAL_WARN_IF( !xRef.is(), "xmloff", "xmloff::AnimationsExporterImpl::convertTarget(), invalid target type!" );
    if( xRef.is() )
    {
        const OUString& rIdentifier = mxExport->getInterfaceToIdentifierMapper().getIdentifier(xRef);
        if( !rIdentifier.isEmpty() )
            sTmp.append( rIdentifier );
    }
}
 
void AnimationsExporterImpl::prepareValue( const Any& rValue )
{
    if( !rValue.hasValue() )
        return;
 
    if( auto pValuePair = o3tl::tryAccess<ValuePair>(rValue) )
    {
        prepareValue( pValuePair->First );
        prepareValue( pValuePair->Second );
    }
    else if( auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue) )
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();
 
        for( nElement = 0; nElement < nLength; nElement++, pAny++ )
            prepareValue( *pAny );
    }
    else if( rValue.getValueTypeClass() == css::uno::TypeClass_INTERFACE )
    {
        Reference< XInterface> xRef( rValue, UNO_QUERY );
        if( xRef.is() )
            mxExport->getInterfaceToIdentifierMapper().registerReference( xRef );
    }
    else if( auto pt = o3tl::tryAccess<ParagraphTarget>(rValue) )
    {
        Reference< XInterface> xRef( getParagraphTarget( *pt ) );
        if( xRef.is() )
            mxExport->getInterfaceToIdentifierMapper().registerReference( xRef );
    }
    else if( auto pEvent = o3tl::tryAccess<Event>(rValue) )
    {
        prepareValue( pEvent->Source );
    }
}
 
AnimationsExporter::AnimationsExporter( SvXMLExport& rExport, const Reference< XPropertySet >& xPageProps )
    : mpImpl( new AnimationsExporterImpl( rExport, xPageProps ) )
{
}
 
AnimationsExporter::~AnimationsExporter()
{
}
 
void AnimationsExporter::prepare( const Reference< XAnimationNode >& xRootNode )
{
    try
    {
        if( xRootNode.is() )
        {
            mpImpl->prepareTransitionNode();
            mpImpl->prepareNode( xRootNode );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
void AnimationsExporter::exportAnimations( const Reference< XAnimationNode >& xRootNode )
{
    try
    {
        if( xRootNode.is() )
        {
            bool bHasEffects = mpImpl->mbHasTransition;
 
            if( !bHasEffects )
            {
                // first check if there are no animations
                Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW );
                Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );
                if( xEnumeration->hasMoreElements() )
                {
                    // first child node may be an empty main sequence, check this
                    Reference< XAnimationNode > xMainNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
                    Reference< XEnumerationAccess > xMainEnumerationAccess( xMainNode, UNO_QUERY_THROW );
                    Reference< XEnumeration > xMainEnumeration( xMainEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );
 
                    // only export if the main sequence is not empty or if there are additional
                    // trigger sequences
                    bHasEffects = xMainEnumeration->hasMoreElements() || xEnumeration->hasMoreElements();
                }
            }
 
            if( bHasEffects )
                mpImpl->exportNode( xRootNode );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}
 
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'registerReference' is required to be utilized.

V530 The return value of function 'registerReference' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'registerReference' is required to be utilized.

V530 The return value of function 'registerReference' is required to be utilized.

V547 Expression 'nTransition != 0' is always false.

V547 Expression 'nTransition == 0' is always true.

V547 Expression 'bLoopSound' is always false.

V560 A part of conditional expression is always false: (nTransition != 0).

V560 A part of conditional expression is always false: (nTransition != 0).

V1019 Compound assignment expression 'aTemp >>= nTemp' is used inside condition.