FileDocCategorySizeDatePackage
TimedElementNode.javaAPI DocphoneME MR2 API (J2ME)17363Wed May 02 18:00:36 BST 2007com.sun.perseus.model

TimedElementNode.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.perseus.model;

import com.sun.perseus.util.SVGConstants;

import org.w3c.dom.DOMException;

import org.w3c.dom.svg.SVGAnimationElement;

import com.sun.perseus.j2d.RenderGraphics;

/**
 * <code>TimedElementNode</code> models <code>ModelNodes</code which 
 * represent Timed Elements.
 *
 *
 * @version $Id: TimedElementNode.java,v 1.6 2006/07/13 00:55:58 st125089 Exp $
 */
public class TimedElementNode extends ElementNode 
                              implements SVGAnimationElement {
    /**
     * The timing support is added by compositing rather than inheritance
     * because Java only allows single inheritance.
     */
    protected TimedElementSupport timedElementSupport;

    /**
     * The timed element's local name.
     */
    protected String localName;

    /**
     * Builds a new timed element that belongs to the given
     * document. This <code>TimedElementNode</code> will belong 
     * to the <code>DocumentNode</code>'s time container.
     *
     * @param ownerDocument the document this node belongs to.
     * @param localName the element's local name
     * @throws IllegalArgumentException if the input ownerDocument is null
     */
    public TimedElementNode(final DocumentNode ownerDocument,
                            final String localName) {
        this(ownerDocument, new TimedElementSupport(), localName);
    }

    /**
     * Builds a new timed element that belongs to the given
     * document. This <code>TimedElementNode</code> will belong 
     * to the <code>DocumentNode</code>'s time container.
     *
     * @param ownerDocument the document this node belongs to.
     * @throws IllegalArgumentException if the input ownerDocument is null
     */
    public TimedElementNode(final DocumentNode ownerDocument) {
        this(ownerDocument, new TimedElementSupport());
    }

    /**
     * Constructor used by derived classes.
     *
     * @param ownerDocument the document this node belongs to.
     * @param timedElementSupport the associated 
     *        <code>TimedElementSupport</code>.
     * @throws IllegalArgumentException if the input ownerDocument is null.
     */
    protected TimedElementNode(final DocumentNode ownerDocument, 
                               final TimedElementSupport timedElementSupport) {
        this(ownerDocument, 
             timedElementSupport,
             // <!> IMPL NOTE : DO THIS WHILE THE REST OF THE ANIMATION
             // IMPLEMENTATION IS PENDING.
             SVGConstants.SVG_SET_TAG);
    }

    /**
     * Constructor used by derived classes.
     *
     * @param ownerDocument the document this node belongs to.
     * @param timedElementSupport the associated 
     *        <code>TimedElementSupport</code>.
     * @param localName the element's local name. Should not be null.
     * @throws IllegalArgumentException if the input ownerDocument is null 
     *         or if the input timedElementSupport is null.
     */
    protected TimedElementNode(final DocumentNode ownerDocument, 
                               final TimedElementSupport timedElementSupport,
                               final String localName) {
        super(ownerDocument);

        if (timedElementSupport == null) {
            throw new IllegalArgumentException();
        }

        if (localName == null) {
            throw new IllegalArgumentException();
        }

        this.timedElementSupport = timedElementSupport;

        this.localName = localName;

        timedElementSupport.animationElement = this;
    }
    
    /**
     * When a TimedElementNode is hooked into the document tree, it needs
     * to register with the closest ancestor TimeContainerNode it can 
     * find. If none, it must register with the root time container.
     */
    void nodeHookedInDocumentTree() {
        super.nodeHookedInDocumentTree();

        ModelNode p = parent;
        while (p != ownerDocument && p != null) {
            if (p instanceof TimeContainerNode) {
                timedElementSupport.setTimeContainer
                    (((TimeContainerNode) p).timeContainerSupport);
                break;
            }
            p = p.parent;
        }

        if (p == ownerDocument) {
            timedElementSupport.setTimeContainer
                (ownerDocument.timeContainerRootSupport);
        }
    }

    /**
     * When a TimedElementNode is unhooked from the document tree, it 
     * needs to unregister from its TimeContainer node. Extentions, such 
     * as Animation, may have to perform additional operations, such as
     * removing themselves from TraitAnim.
     */
    void nodeUnhookedFromDocumentTree() {
        timedElementSupport.setTimeContainer(null);
        timedElementSupport.reset();
    }

    /**
     * @return the animation tag name passed at construction time.
     */
    public String getLocalName() {
        return localName;
    }

    /**
     * The default value for the begin attribute is '0s'.
     *
     * @return an array of trait default values, used if this element
     *         requires that the default trait value be explicitly 
     *         set through a setTrait call. This happens, for example,
     *         with the begin trait value on animation elements.
     */
    public String[][] getDefaultTraits() {
        return new String[][] { {SVGConstants.SVG_BEGIN_ATTRIBUTE, "0s"} };
    }

    /**
     * Used by <code>DocumentNode</code> to create a new instance from
     * a prototype <code>TimedElementNode</code>.
     *
     * @param doc the <code>DocumentNode</code> for which a new node is
     *        should be created.
     * @return a new <code>TimedElementNode</code> for the requested document.
     */
    public ElementNode newInstance(final DocumentNode doc) {
        return new TimedElementNode(doc, new TimedElementSupport(), localName);
    }

    /**
     * @return this node's <code>TimedElementSupport</code>
     */
    public TimedElementSupport getTimedElementSupport() {
        return timedElementSupport;
    }

    /**
     *
     */
    public void beginElementAt(float offset) {
        timedElementSupport.beginAt((long) (offset * 1000));
    }

    /**
     * Creates a begin instance time for the current time.
     */
    public void beginElement() {
        timedElementSupport.beginAt(0);
    }

    /**
     *
     */
    public void endElementAt(float offset) {
        timedElementSupport.endAt((long) (offset * 1000));
    }

    /**
     * Creates an end instance time for the current time.
     */
    public void endElement() {
        timedElementSupport.endAt(0);
    }

    /**
     * Pauses the element. If the element is already paused, this method has no
     * effect. See the SMIL 2 specification for a description of <a
     * href="http://www.w3.org/TR/2001/REC-smil20-20010807/smil20.html#smil-timing-Timing-PausedElementsAndActiveDur">pausing
     * elements</a>.
     */
    public void pauseElement() {
        throw new Error("NOT IMPLEMENTED");
    }

    /**
     * Unpauses the element if it was paused. If the element was not paused,
     * this method has no effect. See the SMIL 2 specification for a description
     * of <a
     * href="http://www.w3.org/TR/2001/REC-smil20-20010807/smil20.html#smil-timing-Timing-PausedElementsAndActiveDur">pausing
     * elements</a>.
     */
    public void unpauseElement() {
        throw new Error("NOT IMPLEMENTED");
    }

    /**
     * @return true if the element is currently paused, false otherwise. See the
     * SMIL 2 specification for a description of <a
     * href="http://www.w3.org/TR/2001/REC-smil20-20010807/smil20.html#smil-timing-Timing-PausedElementsAndActiveDur">pausing
     * elements</a>.
     */
    public boolean getElementPaused() {
        throw new Error("NOT IMPLEMENTED");
    }

    /**
     * TimedElementNode supports the begin, end, dur, min, max, restart,
     * repeatCount, repeatDur and fill attributes.
     *
     * @param traitName the name of the trait which the element may support.
     * @return true if this element supports the given trait in one of the
     *         trait accessor methods (such as <code>getTrait</code> or 
     *         <code>setFloatTrait</code>.
     */
    boolean supportsTrait(final String traitName) {
        if (SVGConstants.SVG_BEGIN_ATTRIBUTE == traitName
            || 
            SVGConstants.SVG_END_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_DUR_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_MIN_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_MAX_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_RESTART_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_REPEAT_COUNT_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_REPEAT_DUR_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FILL_ATTRIBUTE == traitName) {
            return true;
        }

        return super.supportsTrait(traitName);
    }

    // JAVADOC COMMENT ELIDED
    public String getTraitImpl(final String name)
        throws DOMException {
        if (SVGConstants.SVG_BEGIN_ATTRIBUTE == name) {
            if (timedElementSupport.beginConditions.size() == 0) {
                return "0s";
            }
            return TimeCondition.toStringTrait(
                    timedElementSupport.beginConditions);
        } else if (SVGConstants.SVG_END_ATTRIBUTE == name) {
            if (timedElementSupport.endConditions.size() == 0) {
                return SVGConstants.SVG_INDEFINITE_VALUE;
            }
            return TimeCondition.toStringTrait(
                    timedElementSupport.endConditions);
        } else if (SVGConstants.SVG_DUR_ATTRIBUTE == name) {
            return Time.toStringTrait(timedElementSupport.dur);
        } else if (SVGConstants.SVG_MIN_ATTRIBUTE == name) {
            return Time.toStringTrait(timedElementSupport.min);
        } else if (SVGConstants.SVG_MAX_ATTRIBUTE == name) {
            return Time.toStringTrait(timedElementSupport.max);
        } else if (SVGConstants.SVG_RESTART_ATTRIBUTE == name) {
            switch (timedElementSupport.restart) {
            case TimedElementSupport.RESTART_ALWAYS:
                return SVGConstants.SVG_ALWAYS_VALUE;
            case TimedElementSupport.RESTART_WHEN_NOT_ACTIVE:
                return SVGConstants.SVG_WHEN_NOT_ACTIVE_VALUE;
            case TimedElementSupport.RESTART_NEVER:
                return SVGConstants.SVG_NEVER_VALUE;
            default:
                throw new IllegalStateException();
            }
        } else if (SVGConstants.SVG_REPEAT_COUNT_ATTRIBUTE == name) {
            if (Float.isNaN(timedElementSupport.repeatCount)) {
                return null; // Unspecified
            } else if (timedElementSupport.repeatCount == Float.MAX_VALUE) {
                return SVGConstants.SVG_INDEFINITE_VALUE;
            }
            return Float.toString(timedElementSupport.repeatCount);
        } else if (SVGConstants.SVG_REPEAT_DUR_ATTRIBUTE == name) {
            return Time.toStringTrait(timedElementSupport.repeatDur);
        } else if (SVGConstants.SVG_FILL_ATTRIBUTE == name) {
            switch (timedElementSupport.fillBehavior) {
            case TimedElementSupport.FILL_BEHAVIOR_REMOVE:
                return SVGConstants.SVG_REMOVE_VALUE;
            case TimedElementSupport.FILL_BEHAVIOR_FREEZE:
                return SVGConstants.SVG_FREEZE_VALUE;
            default:
                throw new IllegalStateException();
            }
        } else {
            return super.getTraitImpl(name);
        }
    }

    /**
     * Parses the input value an creates TimeCondition instances for the
     * current instance.
     *
     * @param traitName the name of the time condition trait.
     * @param value the trait value.
     * @param isBegin true if this should be parsed as a begin value, i.e., 
     * with a 0s default.
     * @throws DOMException if the input value is invalid.
     */
    protected void parseTimeConditionsTrait(final String traitName,
                                            final String value,
                                            final boolean isBegin) 
        throws DOMException {
        try {
            ownerDocument.timeConditionParser.parseBeginEndAttribute(value,
                                                                     this,
                                                                     isBegin);
        } catch (IllegalArgumentException iae) {
            iae.printStackTrace();
            throw illegalTraitValue(traitName, value);
        }                                                           
    }

    // JAVADOC COMMENT ELIDED
    public void setTraitImpl(final String name, 
                             final String value)
        throws DOMException {
        if (SVGConstants.SVG_BEGIN_ATTRIBUTE == name) {
            checkWriteLoading(name);
            timedElementSupport.beginConditions.removeAllElements();
            parseTimeConditionsTrait(name, value, true);
        } else if (SVGConstants.SVG_END_ATTRIBUTE == name) {
            checkWriteLoading(name);
            timedElementSupport.endConditions.removeAllElements();
            parseTimeConditionsTrait(name, value, false);
        } else if (SVGConstants.SVG_DUR_ATTRIBUTE == name) {
            checkWriteLoading(name);
            // Ignore 'media'
            if (SVGConstants.SVG_MEDIA_VALUE.equals(value)) {
                return;
            }
            timedElementSupport.setDur(parseClockTrait(name, value));
        } else if (SVGConstants.SVG_MIN_ATTRIBUTE == name) {
            checkWriteLoading(name);

            // Ignore 'media'
            if (SVGConstants.SVG_MEDIA_VALUE.equals(value)) {
                return;
            }
            timedElementSupport.setMin(parseMinMaxClock(name, value, true));
        } else if (SVGConstants.SVG_MAX_ATTRIBUTE == name) {
            checkWriteLoading(name);

            // Ignore 'media'
            if (SVGConstants.SVG_MEDIA_VALUE.equals(value)) {
                return;
            }
            timedElementSupport.setMax(parseMinMaxClock(name, value, false));
        } else if (SVGConstants.SVG_RESTART_ATTRIBUTE == name) {
            checkWriteLoading(name);

            if (SVGConstants.SVG_ALWAYS_VALUE.equals(value)) {
                timedElementSupport.restart = 
                    TimedElementSupport.RESTART_ALWAYS;
            } else if (SVGConstants.SVG_WHEN_NOT_ACTIVE_VALUE.equals(value)) {
                timedElementSupport.restart =
                    TimedElementSupport.RESTART_WHEN_NOT_ACTIVE;
            } else if (SVGConstants.SVG_NEVER_VALUE.equals(value)) {
                timedElementSupport.restart =
                    TimedElementSupport.RESTART_NEVER;
            } else {
                throw illegalTraitValue(name, value);
            }
        } else if (SVGConstants.SVG_REPEAT_COUNT_ATTRIBUTE == name) {
            checkWriteLoading(name);

            if (SVGConstants.SVG_INDEFINITE_VALUE.equals(value)) {
                timedElementSupport.repeatCount = Float.MAX_VALUE;
            } else {
                timedElementSupport.repeatCount =
                    parseFloatTrait(name, value);
            }
        } else if (SVGConstants.SVG_REPEAT_DUR_ATTRIBUTE == name) {
            checkWriteLoading(name);
            if (SVGConstants.SVG_INDEFINITE_VALUE.equals(value)) {
                timedElementSupport.repeatDur = Time.INDEFINITE;
            } else {
                timedElementSupport.setRepeatDur(parseClockTrait(name, value));
            }
        } else if (SVGConstants.SVG_FILL_ATTRIBUTE == name) {
            checkWriteLoading(name);
            if (SVGConstants.SVG_REMOVE_VALUE.equals(value)) {
                timedElementSupport.fillBehavior 
                    = TimedElementSupport.FILL_BEHAVIOR_REMOVE;
            } else if (SVGConstants.SVG_FREEZE_VALUE.equals(value)) {
                timedElementSupport.fillBehavior
                    = TimedElementSupport.FILL_BEHAVIOR_FREEZE;
            } else {
                throw illegalTraitValue(name, value);
            }
        } else {
            super.setTraitImpl(name, value);
        }
    }

}