class AnimationManagerStateController extends AbstractModuleStateController {

    @Override
    public void saveState(Node node)
    {
        try
        {
            AnimationPlan plan = myAnimationManager.getCurrentPlan();
            if (plan == null)
            {
                Document doc = node instanceof Document ? (Document)node : node.getOwnerDocument();
                Node timeNode = StateXML.createChildNode(node, doc, node, "/" + ModuleStateController.STATE_QNAME + "/:time",
                        "time");
                StateXML.createChildNode(node, doc, timeNode, "/" + ModuleStateController.STATE_QNAME + "/:time/:animation",
                        "animation");
                return;
            }

            ExportTimeState timeState = new ExportTimeState();

            ExportAnimationState animState = new ExportAnimationState();
            if (myAnimationManager.isPlaying())
            {
                if (myAnimationManager.getAnimationState().getDirection() == Direction.FORWARD)
                {
                    animState.setPlayState(PlayState.FORWARD);
                }
                else
                {
                    animState.setPlayState(PlayState.REVERSE);
                }
            }
            else
            {
                animState.setPlayState(PlayState.STOP);
            }

            animState.setLoopBehavior(LoopBehavior.TAPER_END_TAPER_START);
            animState.setMillisPerFrame(Milliseconds.get(myAnimationManager.getChangeRate()).intValue());

            if (plan instanceof ContinuousAnimationPlan)
            {
                ContinuousAnimationPlan cPlan = (ContinuousAnimationPlan)plan;
                timeState.setAdvanceDuration(cPlan.getAdvanceDuration());
                animState.setLoopInterval(cPlan.getLimitWindow().isTimeless() ? null : cPlan.getLimitWindow());
            }
            else
            {
                TimeSpan start = plan.getAnimationSequence().get(0);
                TimeSpan end = plan.getAnimationSequence().get(plan.getAnimationSequence().size() - 1);
                timeState.setAdvanceDuration(plan.getAnimationSequence().get(0).getDuration());
                animState.setLoopInterval(start.simpleUnion(end));
            }
            timeState.setSequence(plan.getAnimationSequence());

            try
            {
                JAXBContext context = JAXBContextHelper.getCachedContext(ExportTimeState.class);
                context.createMarshaller().marshal(timeState, node);

                XMLUtilities.mergeDuplicateElements(node.getOwnerDocument(), "time");
            }
            catch (JAXBException e)
            {
                LOGGER.error("Failed to marshal time state: " + e, e);
            }

            Node timeNode = (Node)StateXML.newXPath().evaluate("/" + ModuleStateController.STATE_QNAME + "/:time", node,
                    XPathConstants.NODE);

            try
            {
                JAXBContext context = JAXBContextHelper.getCachedContext(ExportAnimationState.class);
                context.createMarshaller().marshal(animState, timeNode);
            }
            catch (JAXBException e)
            {
                LOGGER.error("Failed to marshal animation state: " + e, e);
            }
        }
        catch (XPathExpressionException e)
        {
            LOGGER.error(e, e);
        }
    }

    public  AnimationManagerStateController(AnimationManager animationManager, TimeManager timeManager);

}