You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by jm...@apache.org on 2013/10/08 16:03:43 UTC

[28/62] [abbrv] [partial] Merged Apache Flex 4.9.0 release branch

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/f690ea2f/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedDocumentRoot.java
----------------------------------------------------------------------
diff --git a/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedDocumentRoot.java b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedDocumentRoot.java
new file mode 100644
index 0000000..2b21958
--- /dev/null
+++ b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedDocumentRoot.java
@@ -0,0 +1,288 @@
+/*
+
+   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.
+
+ */
+package org.apache.flex.forks.batik.anim.timing;
+
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.apache.flex.forks.batik.util.DoublyIndexedSet;
+
+/**
+ * An abstract base class for the root time container element
+ * for a document.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id: TimedDocumentRoot.java 580685 2007-09-30 09:07:29Z cam $
+ */
+public abstract class TimedDocumentRoot extends TimeContainer {
+
+    /**
+     * The wallclock time that the document began.
+     */
+    protected Calendar documentBeginTime;
+
+    /**
+     * Allows the use of accessKey() timing specifiers with a single
+     * character, as specified in SVG 1.1.
+     */
+    protected boolean useSVG11AccessKeys;
+
+    /**
+     * Allows the use of accessKey() timing specifiers with a DOM 3
+     * key name, as specified in SVG 1.2.
+     */
+    protected boolean useSVG12AccessKeys;
+
+    /**
+     * A set to determine when propagation of new Instance times should
+     * be stopped.
+     */
+    protected DoublyIndexedSet propagationFlags = new DoublyIndexedSet();
+
+    /**
+     * List of {link TimegraphListener}s to be notified of changes to the
+     * timed elements in this document.
+     */
+    protected LinkedList listeners = new LinkedList();
+
+    /**
+     * Whether the document is currently being sampled.
+     */
+    protected boolean isSampling;
+
+    /**
+     * Whether the document is currently being sampled for a hyperlink.
+     */
+    protected boolean isHyperlinking;
+
+    /**
+     * Creates a new TimedDocumentRoot.
+     * @param useSVG11AccessKeys allows the use of accessKey() timing
+     *                           specifiers with a single character
+     * @param useSVG12AccessKeys allows the use of accessKey() with a
+     *                           DOM 3 key name
+     */
+    public TimedDocumentRoot(boolean useSVG11AccessKeys,
+                             boolean useSVG12AccessKeys) {
+        root = this;
+        this.useSVG11AccessKeys = useSVG11AccessKeys;
+        this.useSVG12AccessKeys = useSVG12AccessKeys;
+    }
+
+    /**
+     * Returns the implicit duration of the element.  The document root
+     * has an {@link #INDEFINITE} implicit duration.
+     */
+    protected float getImplicitDur() {
+        return INDEFINITE;
+    }
+
+    /**
+     * Returns the default begin time for the given child
+     * timed element.  In SVG, this is always 0, since the
+     * only time container is the root SVG element, which acts
+     * like a 'par'.
+     */
+    public float getDefaultBegin(TimedElement child) {
+        return 0.0f;
+    }
+
+    /**
+     * Returns the last sampled document time.
+     */
+    public float getCurrentTime() {
+        return lastSampleTime;
+    }
+
+    /**
+     * Returns whether the document is currently being sampled.
+     */
+    public boolean isSampling() {
+        return isSampling;
+    }
+
+    /**
+     * Returns whether the document is currently being sampled for a hyperlink.
+     */
+    public boolean isHyperlinking() {
+        return isHyperlinking;
+    }
+
+    /**
+     * Samples the entire timegraph at the given time.
+     */
+    public float seekTo(float time, boolean hyperlinking) {
+        // Trace.enter(this, "seekTo", new Object[] { new Float(time) } ); try {
+        isSampling = true;
+        lastSampleTime = time;
+        isHyperlinking = hyperlinking;
+        propagationFlags.clear();
+        // No time containers in SVG, so we don't have to worry
+        // about a partial ordering of timed elements to sample.
+        float mint = Float.POSITIVE_INFINITY;
+        TimedElement[] es = getChildren();
+        for (int i = 0; i < es.length; i++) {
+            float t = es[i].sampleAt(time, hyperlinking);
+            if (t < mint) {
+                mint = t;
+            }
+        }
+        boolean needsUpdates;
+        do {
+            needsUpdates = false;
+            for (int i = 0; i < es.length; i++) {
+                if (es[i].shouldUpdateCurrentInterval) {
+                    needsUpdates = true;
+                    // System.err.print("{" + ((Test.AnimateElement) es[i]).id + "} ");
+                    float t = es[i].sampleAt(time, hyperlinking);
+                    if (t < mint) {
+                        mint = t;
+                    }
+                }
+            }
+        } while (needsUpdates);
+        isSampling = false;
+        if (hyperlinking) {
+            root.currentIntervalWillUpdate();
+        }
+        return mint;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Resets the entire timegraph.
+     */
+    public void resetDocument(Calendar documentBeginTime) {
+        if (documentBeginTime == null) {
+            this.documentBeginTime = Calendar.getInstance();
+        } else {
+            this.documentBeginTime = documentBeginTime;
+        }
+        reset(true);
+    }
+
+    /**
+     * Returns the wallclock time that the document began.
+     */
+    public Calendar getDocumentBeginTime() {
+        return documentBeginTime;
+    }
+
+    /**
+     * Converts an epoch time to document time.
+     */
+    public float convertEpochTime(long t) {
+        long begin = documentBeginTime.getTime().getTime();
+        return (t - begin) / 1000f;
+    }
+
+    /**
+     * Converts a wallclock time to document time.
+     */
+    public float convertWallclockTime(Calendar time) {
+        long begin = documentBeginTime.getTime().getTime();
+        long t = time.getTime().getTime();
+        return (t - begin) / 1000f;
+    }
+
+    /**
+     * Adds a {@link TimegraphListener} to the document.
+     */
+    public void addTimegraphListener(TimegraphListener l) {
+        listeners.add(l);
+    }
+
+    /**
+     * Removes a {@link TimegraphListener} from the document.
+     */
+    public void removeTimegraphListener(TimegraphListener l) {
+        listeners.remove(l);
+    }
+
+    /**
+     * Fires an {@link TimegraphListener#elementAdded} event on all
+     * timegraph listeners.
+     */
+    void fireElementAdded(TimedElement e) {
+        Iterator i = listeners.iterator();
+        while (i.hasNext()) {
+            ((TimegraphListener) i.next()).elementAdded(e);
+        }
+    }
+
+    /**
+     * Fires an {@link TimegraphListener#elementRemoved} event on all
+     * timegraph listeners.
+     */
+    void fireElementRemoved(TimedElement e) {
+        Iterator i = listeners.iterator();
+        while (i.hasNext()) {
+            ((TimegraphListener) i.next()).elementRemoved(e);
+        }
+    }
+
+    // XXX Add fire* methods for the other events in TimegraphListener, and make
+    //     TimedElement fire them.
+
+    /**
+     * Returns whether the specified newly created {@link Interval} should 
+     * propagate its times to the given {@link TimingSpecifier}.
+     * @param i the Interval that has just been created
+     * @param ts the TimingSpecifier that is a dependent of the Interval
+     * @param isBegin whether the dependency is on the begin or end time of
+     *        the Interval
+     */
+    boolean shouldPropagate(Interval i, TimingSpecifier ts, boolean isBegin) {
+        InstanceTime it = isBegin ? i.getBeginInstanceTime()
+                                  : i.getEndInstanceTime();
+        if (propagationFlags.contains(it, ts)) {
+            return false;
+        }
+        propagationFlags.add(it, ts);
+        return true;
+    }
+
+    /**
+     * Invoked by timed elements in this document to indicate that the current
+     * interval will be re-evaluated at the next sample.  This should be
+     * overridden in a concrete class so that ticks can be scheduled immediately
+     * if they are currently paused due to no animations being active.
+     */
+    protected void currentIntervalWillUpdate() {
+    }
+
+    /**
+     * Returns the namespace URI of the event that corresponds to the given
+     * animation event name.
+     */
+    protected abstract String getEventNamespaceURI(String eventName);
+
+    /**
+     * Returns the type of the event that corresponds to the given
+     * animation event name.
+     */
+    protected abstract String getEventType(String eventName);
+
+    /**
+     * Returns the name of the repeat event.
+     * @return either "repeat" or "repeatEvent"
+     */
+    protected abstract String getRepeatEventName();
+}

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/f690ea2f/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedElement.java
----------------------------------------------------------------------
diff --git a/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedElement.java b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedElement.java
new file mode 100644
index 0000000..bc73529
--- /dev/null
+++ b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimedElement.java
@@ -0,0 +1,1575 @@
+/*
+
+   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.
+
+ */
+package org.apache.flex.forks.batik.anim.timing;
+
+import java.util.*;
+
+import org.apache.flex.forks.batik.anim.AnimationException;
+import org.apache.flex.forks.batik.i18n.LocalizableSupport;
+import org.apache.flex.forks.batik.parser.ClockHandler;
+import org.apache.flex.forks.batik.parser.ClockParser;
+import org.apache.flex.forks.batik.parser.ParseException;
+import org.apache.flex.forks.batik.util.SMILConstants;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventTarget;
+
+/**
+ * An abstract base class for elements that can have timing applied to them.
+ * The concrete versions of this class do not necessarily have to be the
+ * same as the DOM class, and in fact, this will mostly be impossible unless
+ * creating new DOM classes that inherit from these elements.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id: TimedElement.java 592593 2007-11-07 00:44:30Z cam $
+ */
+public abstract class TimedElement implements SMILConstants {
+
+    // Constants for fill mode.
+    public static final int FILL_REMOVE = 0;
+    public static final int FILL_FREEZE = 1;
+
+    // Constants for restart mode.
+    public static final int RESTART_ALWAYS          = 0;
+    public static final int RESTART_WHEN_NOT_ACTIVE = 1;
+    public static final int RESTART_NEVER           = 2;
+
+    // Constants for time values.
+    public static final float INDEFINITE = Float.POSITIVE_INFINITY;
+    public static final float UNRESOLVED = Float.NaN;
+
+    /**
+     * The root time container.
+     */
+    protected TimedDocumentRoot root;
+
+    /**
+     * The parent time container.
+     */
+    protected TimeContainer parent;
+
+    /**
+     * Timing specifiers for the begin times of this element.
+     */
+    protected TimingSpecifier[] beginTimes;
+
+    /**
+     * Timing specifiers for the end times of this element.
+     */
+    protected TimingSpecifier[] endTimes;
+
+    /**
+     * Duration of this element, if {@link #durMedia} <code>= false</code>.
+     * If unspecified, it will be {@link #UNRESOLVED}.
+     */
+    protected float simpleDur;
+
+    /**
+     * Whether the simple duration of this element should be equal
+     * to the implicit duration.
+     */
+    protected boolean durMedia;
+
+    /**
+     * The number of repeats.  If unspecified, it will be
+     * {@link #UNRESOLVED}.
+     */
+    protected float repeatCount;
+
+    /**
+     * The duration of repeats.  If unspecified, it will be
+     * {@link #UNRESOLVED}.
+     */
+    protected float repeatDur;
+
+    /**
+     * The current repeat iteration.
+     */
+    protected int currentRepeatIteration;
+
+    /**
+     * The local active time of the last repeat.
+     */
+    protected float lastRepeatTime;
+
+    /**
+     * The fill mode for this element.  Uses the FILL_* constants
+     * defined in this class.
+     */
+    protected int fillMode;
+
+    /**
+     * The restart mode for this element.  Uses the RESTART_* constants
+     * defined in this class.
+     */
+    protected int restartMode;
+
+    /**
+     * The minimum active duration of this element.  If {@link #minMedia}
+     * <code>= true</code>, it will be <code>0f</code>.
+     */
+    protected float min;
+
+    /**
+     * Whether the min value was specified as 'media'.
+     */
+    protected boolean minMedia;
+
+    /**
+     * The maximum active duration of this element.  If {@link #maxMedia}
+     * <code>= true</code>, it will be {@link #INDEFINITE}.
+     */
+    protected float max;
+
+    /**
+     * Whether the max value was specified as 'media'.
+     */
+    protected boolean maxMedia;
+
+    /**
+     * Whether the element is currently active.
+     */
+    protected boolean isActive;
+
+    /**
+     * Whether the element is currently frozen.
+     */
+    protected boolean isFrozen;
+
+    /**
+     * The current time of this element in local active time.
+     */
+    protected float lastSampleTime;
+
+    /**
+     * The computed repeat duration of the element.
+     */
+    protected float repeatDuration;
+
+    /**
+     * List of begin InstanceTimes.
+     */
+    protected List beginInstanceTimes = new ArrayList();
+
+    /**
+     * List of end InstanceTimes.
+     */
+    protected List endInstanceTimes = new ArrayList();
+
+    /**
+     * The current Interval.
+     */
+    protected Interval currentInterval;
+
+    /**
+     * The end time of the previous interval, initially
+     * {@link Float#NEGATIVE_INFINITY}.
+     */
+    protected float lastIntervalEnd;
+
+    /**
+     * List of previous intervals.
+     */
+    // protected LinkedList previousIntervals = new LinkedList();
+
+    /**
+     * The previous interval.
+     */
+    protected Interval previousInterval;
+
+    /**
+     * List of TimingSpecifiers on other elements that depend on this
+     * element's begin times.
+     */
+    protected LinkedList beginDependents = new LinkedList();
+
+    /**
+     * List of TimingSpecifiers on other elements that depend on this
+     * element's end times.
+     */
+    protected LinkedList endDependents = new LinkedList();
+
+    /**
+     * Whether the list of instance times should be checked to update
+     * the current interval.
+     */
+    protected boolean shouldUpdateCurrentInterval = true;
+
+    /**
+     * Whether this timed element has parsed its timing attributes yet.
+     */
+    protected boolean hasParsed;
+
+    /**
+     * Map of {@link Event} objects to {@link HashSet}s of {@link
+     * TimingSpecifier}s that caught them.
+     */
+    protected Map handledEvents = new HashMap();
+
+    /**
+     * Whether this timed element is currently being sampled.
+     */
+    protected boolean isSampling;
+
+    /**
+     * Whether an instance time update message has already been propagated to
+     * this timed element.
+     */
+    protected boolean hasPropagated;
+
+    /**
+     * Creates a new TimedElement.
+     */
+    public TimedElement() {
+        beginTimes = new TimingSpecifier[0];
+        endTimes = beginTimes;
+        simpleDur = UNRESOLVED;
+        repeatCount = UNRESOLVED;
+        repeatDur = UNRESOLVED;
+        lastRepeatTime = UNRESOLVED;
+        max = INDEFINITE;
+        lastSampleTime = UNRESOLVED;
+        lastIntervalEnd = Float.NEGATIVE_INFINITY;
+    }
+
+    /**
+     * Returns the root time container of this timed element.
+     */
+    public TimedDocumentRoot getRoot() {
+        return root;
+    }
+
+    /**
+     * Returns the current active time of this element.
+     */
+    public float getActiveTime() {
+        return lastSampleTime;
+    }
+
+    /**
+     * Returns the current simple time of this element.
+     */
+    public float getSimpleTime() {
+        return lastSampleTime - lastRepeatTime;
+    }
+
+    /**
+     * Called by a TimingSpecifier of this element when a new
+     * InstanceTime is created.  This will be in response to an event
+     * firing, a DOM method being called or a new Instance being
+     * created by a syncbase element.
+     */
+    protected float addInstanceTime(InstanceTime time, boolean isBegin) {
+        // Trace.enter(this, "addInstanceTime", new Object[] { time, new Boolean(isBegin) } ); try {
+        hasPropagated = true;
+        List instanceTimes = isBegin ? beginInstanceTimes : endInstanceTimes;
+        int index = Collections.binarySearch(instanceTimes, time);
+        if (index < 0) {
+            index = -(index + 1);
+        }
+        instanceTimes.add(index, time);
+        shouldUpdateCurrentInterval = true;
+        float ret;
+        if (root.isSampling() && !isSampling) {
+            ret = sampleAt(root.getCurrentTime(), root.isHyperlinking());
+        } else {
+            ret = Float.POSITIVE_INFINITY;
+        }
+        hasPropagated = false;
+        root.currentIntervalWillUpdate();
+        return ret;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Called by a TimingSpecifier of this element when an InstanceTime
+     * should be removed.  This will be in response to the pruning of an
+     * Interval.
+     */
+    protected float removeInstanceTime(InstanceTime time, boolean isBegin) {
+        // Trace.enter(this, "removeInstanceTime", new Object[] { time, new Boolean(isBegin) } ); try {
+        hasPropagated = true;
+        List instanceTimes = isBegin ? beginInstanceTimes : endInstanceTimes;
+        int index = Collections.binarySearch(instanceTimes, time);
+        for (int i = index; i >= 0; i--) {
+            InstanceTime it = (InstanceTime) instanceTimes.get(i);
+            if (it == time) {
+                instanceTimes.remove(i);
+                break;
+            }
+            if (it.compareTo(time) != 0) {
+                break;
+            }
+        }
+        int len = instanceTimes.size();
+        for (int i = index + 1; i < len; i++) {
+            InstanceTime it = (InstanceTime) instanceTimes.get(i);
+            if (it == time) {
+                instanceTimes.remove(i);
+                break;
+            }
+            if (it.compareTo(time) != 0) {
+                break;
+            }
+        }
+        shouldUpdateCurrentInterval = true;
+        float ret;
+        if (root.isSampling() && !isSampling) {
+            ret = sampleAt(root.getCurrentTime(), root.isHyperlinking());
+        } else {
+            ret = Float.POSITIVE_INFINITY;
+        }
+        hasPropagated = false;
+        root.currentIntervalWillUpdate();
+        return ret;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Called by a TimingSpecifier of this element when an InstanceTime
+     * has been updated.  This will be in response to a dependent
+     * syncbase change.
+     */
+    protected float instanceTimeChanged(InstanceTime time, boolean isBegin) {
+        // Trace.enter(this, "instanceTimeChanged", new Object[] { time, new Boolean(isBegin) } ); try {
+        hasPropagated = true;
+        shouldUpdateCurrentInterval = true;
+        float ret;
+        if (root.isSampling() && !isSampling) {
+            ret = sampleAt(root.getCurrentTime(), root.isHyperlinking());
+        } else {
+            ret = Float.POSITIVE_INFINITY;
+        }
+        hasPropagated = false;
+        return ret;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Adds a dependent TimingSpecifier for this element.
+     */
+    protected void addDependent(TimingSpecifier dependent, boolean forBegin) {
+        // Trace.enter(this, "addDependent", new Object[] { dependent, new Boolean(forBegin) } ); try {
+        if (forBegin) {
+            beginDependents.add(dependent);
+        } else {
+            endDependents.add(dependent);
+        }
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Removes a dependent TimingSpecifier for this element.
+     */
+    protected void removeDependent(TimingSpecifier dependent,
+                                   boolean forBegin) {
+        // Trace.enter(this, "removeDependent", new Object[] { dependent, new Boolean(forBegin) } ); try {
+        if (forBegin) {
+            beginDependents.remove(dependent);
+        } else {
+            endDependents.remove(dependent);
+        }
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Returns the simple duration time of this element.
+     */
+    public float getSimpleDur() {
+        if (durMedia) {
+            return getImplicitDur();
+        } else if (isUnresolved(simpleDur)) {
+            if (isUnresolved(repeatCount) && isUnresolved(repeatDur)
+                    && endTimes.length > 0) {
+                return INDEFINITE;
+            }
+            return getImplicitDur();
+        } else {
+            return simpleDur;
+        }
+    }
+
+    /**
+     * Returns whether the given time value is equal to the
+     * {@link #UNRESOLVED} value.
+     */
+    public static boolean isUnresolved(float t) {
+        return Float.isNaN(t);
+    }
+
+    /**
+     * Returns the active duration time of this element.
+     */
+    public float getActiveDur(float B, float end) {
+        float d = getSimpleDur();
+        float PAD;
+        if (!isUnresolved(end) && d == INDEFINITE) {
+            PAD = minusTime(end, B);
+            repeatDuration = minTime(max, maxTime(min, PAD));
+            return repeatDuration;
+        }
+
+        float IAD;
+        if (d == 0) {
+            IAD = 0;
+        } else {
+            if (isUnresolved(repeatDur) && isUnresolved(repeatCount)) {
+                IAD = d;
+            } else {
+                float p1 = isUnresolved(repeatCount)
+                                ? INDEFINITE
+                                : multiplyTime(d, repeatCount);
+                float p2 = isUnresolved(repeatDur)
+                                ? INDEFINITE
+                                : repeatDur;
+                IAD = minTime(minTime(p1, p2), INDEFINITE);
+            }
+        }
+        if (isUnresolved(end) || end == INDEFINITE) {
+            PAD = IAD;
+        } else {
+            PAD = minTime(IAD, minusTime(end, B));
+        }
+        repeatDuration = IAD;
+        return minTime(max, maxTime(min, PAD));
+    }
+
+    /**
+     * Subtracts one simple time from another.
+     */
+    protected float minusTime(float t1, float t2) {
+        if (isUnresolved(t1) || isUnresolved(t2)) {
+            return UNRESOLVED;
+        }
+        if (t1 == INDEFINITE || t2 == INDEFINITE) {
+            return INDEFINITE;
+        }
+        return t1 - t2;
+    }
+
+    /**
+     * Multiplies one simple time by n.
+     */
+    protected float multiplyTime(float t, float n) {
+        if (isUnresolved(t) || t == INDEFINITE) {
+            return t;
+        }
+        return t * n;
+    }
+
+    /**
+     * Returns the minimum of two time values.
+     */
+    protected float minTime(float t1, float t2) {
+        if (t1 == 0.0f || t2 == 0.0f) {
+            return 0.0f;
+        }
+        if ((t1 == INDEFINITE || isUnresolved(t1))
+                && t2 != INDEFINITE && !isUnresolved(t2)) {
+            return t2;
+        }
+        if ((t2 == INDEFINITE || isUnresolved(t2))
+                && t1 != INDEFINITE && !isUnresolved(t1)) {
+            return t1;
+        }
+        if (t1 == INDEFINITE && isUnresolved(t2)
+                || isUnresolved(t1) && t2 == INDEFINITE) {
+            return INDEFINITE;
+        }
+        if (t1 < t2) {
+            return t1;
+        }
+        return t2;
+    }
+
+    /**
+     * Returns the maximum of two time values.
+     */
+    protected float maxTime(float t1, float t2) {
+        if ((t1 == INDEFINITE || isUnresolved(t1))
+                && t2 != INDEFINITE && !isUnresolved(t2)) {
+            return t1;
+        }
+        if ((t2 == INDEFINITE || isUnresolved(t2))
+                && t1 != INDEFINITE && !isUnresolved(t1)) {
+            return t2;
+        }
+        if (t1 == INDEFINITE && isUnresolved(t2)
+                || isUnresolved(t1) && t2 == INDEFINITE) {
+            return UNRESOLVED;
+        }
+        if (t1 > t2) {
+            return t1;
+        }
+        return t2;
+    }
+
+    /**
+     * Returns the implicit duration of the element.  Currently, nested time
+     * containers are not supported by SVG so this just returns
+     * {@link #UNRESOLVED} by default.  This should be overriden in derived
+     * classes that play media, since they will have an implicit duration.
+     */
+    protected float getImplicitDur() {
+        return UNRESOLVED;
+    }
+
+    /**
+     * Notifies dependents of a new interval.
+     */
+    protected float notifyNewInterval(Interval interval) {
+        // Trace.enter(this, "notifyNewInterval", new Object[] { interval } ); try {
+        float dependentMinTime = Float.POSITIVE_INFINITY;
+        Iterator i = beginDependents.iterator();
+        while (i.hasNext()) {
+            TimingSpecifier ts = (TimingSpecifier) i.next();
+            // Trace.print(ts.owner + "'s " + (ts.isBegin ? "begin" : "end" ) + ": " + ts);
+            float t = ts.newInterval(interval);
+            if (t < dependentMinTime) {
+                dependentMinTime = t;
+            }
+        }
+        i = endDependents.iterator();
+        while (i.hasNext()) {
+            TimingSpecifier ts = (TimingSpecifier) i.next();
+            // Trace.print(ts.owner + "'s " + (ts.isBegin ? "begin" : "end" ) + ": " + ts);
+            float t = ts.newInterval(interval);
+            if (t < dependentMinTime) {
+                dependentMinTime = t;
+            }
+        }
+        return dependentMinTime;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Notifies dependents of a removed interval.
+     */
+    protected float notifyRemoveInterval(Interval interval) {
+        // Trace.enter(this, "notifyRemoveInterval", new Object[] { interval } ); try {
+        float dependentMinTime = Float.POSITIVE_INFINITY;
+        Iterator i = beginDependents.iterator();
+        while (i.hasNext()) {
+            TimingSpecifier ts = (TimingSpecifier) i.next();
+            float t = ts.removeInterval(interval);
+            if (t < dependentMinTime) {
+                dependentMinTime = t;
+            }
+        }
+        i = endDependents.iterator();
+        while (i.hasNext()) {
+            TimingSpecifier ts = (TimingSpecifier) i.next();
+            float t = ts.removeInterval(interval);
+            if (t < dependentMinTime) {
+                dependentMinTime = t;
+            }
+        }
+        return dependentMinTime;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Calculates the local simple time.  Currently the hyperlinking parameter
+     * is ignored, so DOM timing events are fired during hyperlinking seeks.
+     * If we were following SMIL 2.1 rather than SMIL Animation, then these
+     * events would have to be suppressed.
+     *
+     * @return the number of seconds until this element becomes active again
+     *         if it currently is not, {@link Float#POSITIVE_INFINITY} if this
+     *         element will become active at some undetermined point in the
+     *         future (because of unresolved begin times, for example) or
+     *         will never become active again, or <code>0f</code> if the
+     *         element is currently active.
+     */
+    protected float sampleAt(float parentSimpleTime, boolean hyperlinking) {
+        // Trace.enter(this, "sampleAt", new Object[] { new Float(parentSimpleTime) } ); try {
+        isSampling = true;
+
+        float time = parentSimpleTime; // No time containers in SVG.
+
+        // First, process any events that occurred since the last sampling,
+        // taking into account event sensitivity.
+        Iterator i = handledEvents.entrySet().iterator();
+        while (i.hasNext()) {
+            Map.Entry e = (Map.Entry) i.next();
+            Event evt = (Event) e.getKey();
+            Set ts = (Set) e.getValue();
+            Iterator j = ts.iterator();
+            boolean hasBegin = false, hasEnd = false;
+            while (j.hasNext() && !(hasBegin && hasEnd)) {
+                EventLikeTimingSpecifier t =
+                    (EventLikeTimingSpecifier) j.next();
+                if (t.isBegin()) {
+                    hasBegin = true;
+                } else {
+                    hasEnd = true;
+                }
+            }
+            boolean useBegin, useEnd;
+            if (hasBegin && hasEnd) {
+                useBegin = !isActive || restartMode == RESTART_ALWAYS;
+                useEnd = !useBegin;
+            } else if (hasBegin && (!isActive ||
+                        restartMode == RESTART_ALWAYS)) {
+                useBegin = true;
+                useEnd = false;
+            } else if (hasEnd && isActive) {
+                useBegin = false;
+                useEnd = true;
+            } else {
+                continue;
+            }
+            j = ts.iterator();
+            while (j.hasNext()) {
+                EventLikeTimingSpecifier t =
+                    (EventLikeTimingSpecifier) j.next();
+                boolean isBegin = t.isBegin();
+                if (isBegin && useBegin || !isBegin && useEnd) {
+                    t.resolve(evt);
+                    shouldUpdateCurrentInterval = true;
+                }
+            }
+        }
+        handledEvents.clear();
+
+        // Now process intervals.
+        if (currentInterval != null) {
+            float begin = currentInterval.getBegin();
+            if (lastSampleTime < begin && time >= begin) {
+                if (!isActive) {
+                    toActive(begin);
+                }
+                isActive = true;
+                isFrozen = false;
+                lastRepeatTime = begin;
+                fireTimeEvent
+                    (SMIL_BEGIN_EVENT_NAME, currentInterval.getBegin(), 0);
+            }
+        }
+        // For each sample, we might need to update the current interval's
+        // begin and end times, or end the current interval and compute
+        // a new one.
+        boolean hasEnded = currentInterval != null
+            && time >= currentInterval.getEnd();
+        // Fire any repeat events that should have been fired since the
+        // last sample.
+        if (currentInterval != null) {
+            float begin = currentInterval.getBegin();
+            if (time >= begin) {
+                float d = getSimpleDur();
+                while (time - lastRepeatTime >= d
+                        && lastRepeatTime + d < begin + repeatDuration) {
+                    lastRepeatTime += d;
+                    currentRepeatIteration++;
+                    fireTimeEvent(root.getRepeatEventName(), lastRepeatTime,
+                                  currentRepeatIteration);
+                }
+            }
+        }
+
+        // Trace.print("begin loop");
+        float dependentMinTime = Float.POSITIVE_INFINITY;
+        if (hyperlinking) {
+            shouldUpdateCurrentInterval = true;
+        }
+        while (shouldUpdateCurrentInterval || hasEnded) {
+            if (hasEnded) {
+                // previousIntervals.add(currentInterval);
+                previousInterval = currentInterval;
+                isActive = false;
+                isFrozen = fillMode == FILL_FREEZE;
+                toInactive(false, isFrozen);
+                fireTimeEvent(SMIL_END_EVENT_NAME, currentInterval.getEnd(), 0);
+            }
+            boolean first =
+                // currentInterval == null && previousIntervals.isEmpty();
+                currentInterval == null && previousInterval == null;
+            if (currentInterval != null && hyperlinking) {
+                // Hyperlinking, so remove the current interval and force a new
+                // one to be computed.
+                isActive = false;
+                isFrozen = false;
+                toInactive(false, false);
+                currentInterval = null;
+                // fireTimeEvent(SMIL_END_EVENT_NAME, currentInterval.getEnd(), 0);
+            }
+            if (currentInterval == null || hasEnded) {
+                if (first || hyperlinking || restartMode != RESTART_NEVER) {
+                    float beginAfter;
+                    boolean incl = true;
+                    if (first || hyperlinking) {
+                        beginAfter = Float.NEGATIVE_INFINITY;
+                    } else {
+                        // beginAfter = ((Interval) previousIntervals.getLast()).getEnd();
+                        beginAfter = previousInterval.getEnd();
+                        incl = beginAfter != previousInterval.getBegin();
+                    }
+                    Interval interval =
+                        computeInterval(first, false, beginAfter, incl);
+                    if (interval == null) {
+                        currentInterval = null;
+                    } else {
+                        float dmt = selectNewInterval(time, interval);
+                        if (dmt < dependentMinTime) {
+                            dependentMinTime = dmt;
+                        }
+                    }
+                } else {
+                    currentInterval = null;
+                }
+            } else {
+                float currentBegin = currentInterval.getBegin();
+                if (currentBegin > time) {
+                    // Interval hasn't started yet.
+                    float beginAfter;
+                    boolean incl = true;
+                    // if (previousIntervals.isEmpty()) {
+                    if (previousInterval == null) {
+                        beginAfter = Float.NEGATIVE_INFINITY;
+                    } else {
+                        // beginAfter = ((Interval) previousIntervals.getLast()).getEnd();
+                        beginAfter = previousInterval.getEnd();
+                        incl = beginAfter != previousInterval.getBegin();
+                    }
+                    Interval interval =
+                        computeInterval(false, false, beginAfter, incl);
+                    float dmt = notifyRemoveInterval(currentInterval);
+                    if (dmt < dependentMinTime) {
+                        dependentMinTime = dmt;
+                    }
+                    if (interval == null) {
+                        currentInterval = null;
+                    } else {
+                        dmt = selectNewInterval(time, interval);
+                        if (dmt < dependentMinTime) {
+                            dependentMinTime = dmt;
+                        }
+                    }
+                } else {
+                    // Interval has already started.
+                    Interval interval =
+                        computeInterval(false, true, currentBegin, true);
+                    float newEnd = interval.getEnd();
+                    if (currentInterval.getEnd() != newEnd) {
+                        float dmt =
+                            currentInterval.setEnd
+                                (newEnd, interval.getEndInstanceTime());
+                        if (dmt < dependentMinTime) {
+                            dependentMinTime = dmt;
+                        }
+                    }
+                }
+            }
+            shouldUpdateCurrentInterval = false;
+            hyperlinking = false;
+            hasEnded = currentInterval != null && time >= currentInterval.getEnd();
+        }
+        // Trace.print("end loop");
+
+        float d = getSimpleDur();
+        if (isActive && !isFrozen) {
+            if (time - currentInterval.getBegin() >= repeatDuration) {
+                // Trace.print("element between repeat and active duration");
+                isFrozen = fillMode == FILL_FREEZE;
+                toInactive(true, isFrozen);
+            } else {
+                // Trace.print("element active, sampling at simple time " + (time - lastRepeatTime));
+                sampledAt(time - lastRepeatTime, d, currentRepeatIteration);
+            }
+        }
+        if (isFrozen) {
+            float t;
+            boolean atLast;
+            if (isActive) {
+                t = currentInterval.getBegin() + repeatDuration - lastRepeatTime;
+                atLast = lastRepeatTime + d == currentInterval.getBegin() + repeatDuration;
+            } else {
+                // Interval previousInterval = (Interval) previousIntervals.getLast();
+                t = previousInterval.getEnd() - lastRepeatTime;
+                atLast = lastRepeatTime + d == previousInterval.getEnd();
+            }
+            if (atLast) {
+                // Trace.print("element frozen" + (isActive ? " (but still active)" : "") + ", sampling last value");
+                sampledLastValue(currentRepeatIteration);
+            } else {
+                // Trace.print("element frozen" + (isActive ? " (but still active)" : "") + ", sampling at simple time " + (t % d));
+                sampledAt(t % d, d, currentRepeatIteration);
+            }
+        } else if (!isActive) {
+            // Trace.print("element not sampling");
+        }
+
+        isSampling = false;
+
+        lastSampleTime = time;
+        if (currentInterval != null) {
+            float t = currentInterval.getBegin() - time;
+            if (t <= 0) {
+                t = isConstantAnimation() || isFrozen ? currentInterval.getEnd() - time : 0;
+            }
+            if (dependentMinTime < t) {
+                return dependentMinTime;
+            }
+            return t;
+        }
+        return dependentMinTime;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Returns whether the end timing specifier list contains any eventbase,
+     * accesskey or repeat timing specifiers.
+     */
+    protected boolean endHasEventConditions() {
+        for (int i = 0; i < endTimes.length; i++) {
+            if (endTimes[i].isEventCondition()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the current interval to the one specified.  This will notify
+     * dependents and fire the 'begin' and any necessary 'repeat' events.
+     * @param time the current sampling time
+     * @param interval the Interval object to select to be current
+     * @return the minimum time the animation engine can safely wait, as
+     *         determined by dependents of the interval
+     */
+    protected float selectNewInterval(float time, Interval interval) {
+        // Trace.enter(this, "selectNewInterval", new Object[] { interval }); try {
+        currentInterval = interval;
+        float dmt = notifyNewInterval(currentInterval);
+        float beginEventTime = currentInterval.getBegin();
+        if (time >= beginEventTime) {
+            lastRepeatTime = beginEventTime;
+            if (beginEventTime < 0) {
+                beginEventTime = 0;
+            }
+            toActive(beginEventTime);
+            isActive = true;
+            isFrozen = false;
+            fireTimeEvent(SMIL_BEGIN_EVENT_NAME, beginEventTime, 0);
+            float d = getSimpleDur();
+            float end = currentInterval.getEnd();
+            while (time - lastRepeatTime >= d
+                    && lastRepeatTime + d < end) {
+                lastRepeatTime += d;
+                currentRepeatIteration++;
+                fireTimeEvent(root.getRepeatEventName(), lastRepeatTime,
+                              currentRepeatIteration);
+            }
+        }
+        return dmt;
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Computes an interval from the begin and end instance time lists.
+     * @param first indicates whether this is the first interval to compute
+     * @param fixedBegin if true, specifies that the value given for
+     *                   <code>beginAfter</code> is taken to be the actual begin
+     *                   time for the interval; only the end value is computed.
+     * @param beginAfter the earliest possible begin time for the computed
+     *                   interval.
+     * @param incl if true (and <code>!fixedBegin</code>), specifies that the
+     *             new interval's begin time must be greater than
+     *             <code>beginAfter</code>; otherwise, the begin time must be
+     *             greater than or equal to <code>beginAfter</code>.
+     */
+    protected Interval computeInterval(boolean first, boolean fixedBegin,
+                                       float beginAfter, boolean incl) {
+        // Trace.enter(this, "computeInterval", new Object[] { new Boolean(first), new Boolean(fixedBegin), new Float(beginAfter)} ); try {
+        // Trace.print("computing interval from begins=" + beginInstanceTimes + ", ends=" + endInstanceTimes);
+        Iterator beginIterator = beginInstanceTimes.iterator();
+        Iterator endIterator = endInstanceTimes.iterator();
+        float parentSimpleDur = parent.getSimpleDur();
+        InstanceTime endInstanceTime = endIterator.hasNext()
+            ? (InstanceTime) endIterator.next()
+            : null;
+        boolean firstEnd = true;
+        InstanceTime beginInstanceTime = null;
+        InstanceTime nextBeginInstanceTime = null;
+        for (;;) {
+            float tempBegin;
+            if (fixedBegin) {
+                tempBegin = beginAfter;
+                while (beginIterator.hasNext()) {
+                    nextBeginInstanceTime = (InstanceTime) beginIterator.next();
+                    if (nextBeginInstanceTime.getTime() > tempBegin) {
+                        break;
+                    }
+                }
+            } else {
+                for (;;) {
+                    if (!beginIterator.hasNext()) {
+                        // ran out of begin values
+                        // Trace.print("returning null interval");
+                        return null;
+                    }
+                    beginInstanceTime = (InstanceTime) beginIterator.next();
+                    tempBegin = beginInstanceTime.getTime();
+                    if (incl && tempBegin >= beginAfter
+                            || !incl && tempBegin > beginAfter) {
+                        if (beginIterator.hasNext()) {
+                            nextBeginInstanceTime =
+                                (InstanceTime) beginIterator.next();
+                            if (beginInstanceTime.getTime()
+                                    == nextBeginInstanceTime.getTime()) {
+                                // XXX Not sure if this is exactly correct to
+                                //     skip past these identical times, but it
+                                //     avoids an infinite loop of 0s intervals
+                                //     being created.
+                                nextBeginInstanceTime = null;
+                                continue;
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            if (tempBegin >= parentSimpleDur) {
+                // the begin value is after the parent has ended
+                // Trace.print("returning null interval");
+                return null;
+            }
+            float tempEnd;
+            if (endTimes.length == 0) {
+                // no 'end' attribute specified
+                tempEnd = tempBegin + getActiveDur(tempBegin, INDEFINITE);
+                // Trace.print("no end specified, so tempEnd = " + tempEnd);
+            } else {
+                if (endInstanceTimes.isEmpty()) {
+                    tempEnd = UNRESOLVED;
+                } else {
+                    tempEnd = endInstanceTime.getTime();
+                    if (first && !firstEnd && tempEnd == tempBegin
+                            || !first && currentInterval != null
+                                && tempEnd == currentInterval.getEnd()
+                                && (incl && beginAfter >= tempEnd
+                                        || !incl && beginAfter > tempEnd)) {
+                        for (;;) {
+                            if (!endIterator.hasNext()) {
+                                if (endHasEventConditions()) {
+                                    tempEnd = UNRESOLVED;
+                                    break;
+                                }
+                                // Trace.print("returning null interval");
+                                return null;
+                            }
+                            endInstanceTime = (InstanceTime) endIterator.next();
+                            tempEnd = endInstanceTime.getTime();
+                            if (tempEnd > tempBegin) {
+                                break;
+                            }
+                        }
+                    }
+                    firstEnd = false;
+                    for (;;) {
+                        if (tempEnd >= tempBegin) {
+                            break;
+                        }
+                        if (!endIterator.hasNext()) {
+                            if (endHasEventConditions()) {
+                                tempEnd = UNRESOLVED;
+                                break;
+                            }
+                            // Trace.print("returning null interval");
+                            return null;
+                        }
+                        endInstanceTime = (InstanceTime) endIterator.next();
+                        tempEnd = endInstanceTime.getTime();
+                    }
+                }
+                float ad = getActiveDur(tempBegin, tempEnd);
+                tempEnd = tempBegin + ad;
+            }
+            if (!first || tempEnd > 0 || tempBegin == 0 && tempEnd == 0
+                    || isUnresolved(tempEnd)) {
+                // Trace.print("considering restart semantics");
+                if (restartMode == RESTART_ALWAYS
+                        && nextBeginInstanceTime != null) {
+                    float nextBegin = nextBeginInstanceTime.getTime();
+                    // Trace.print("nextBegin == " + nextBegin);
+                    if (nextBegin < tempEnd || isUnresolved(tempEnd)) {
+                        tempEnd = nextBegin;
+                        endInstanceTime = nextBeginInstanceTime;
+                    }
+                }
+                Interval i = new Interval(tempBegin, tempEnd,
+                                          beginInstanceTime, endInstanceTime);
+                // Trace.print("returning interval: " + i);
+                return i;
+            }
+            if (fixedBegin) {
+                // Trace.print("returning null interval");
+                return null;
+            }
+            beginAfter = tempEnd;
+        }
+        // } finally { Trace.exit(); }
+    }
+
+    /**
+     * Resets this element.
+     */
+    protected void reset(boolean clearCurrentBegin) {
+        Iterator i = beginInstanceTimes.iterator();
+        while (i.hasNext()) {
+            InstanceTime it = (InstanceTime) i.next();
+            if (it.getClearOnReset() &&
+                    (clearCurrentBegin
+                        || currentInterval == null
+                        || currentInterval.getBeginInstanceTime() != it)) {
+                i.remove();
+            }
+        }
+        i = endInstanceTimes.iterator();
+        while (i.hasNext()) {
+            InstanceTime it = (InstanceTime) i.next();
+            if (it.getClearOnReset()) {
+                i.remove();
+            }
+        }
+        if (isFrozen) {
+            removeFill();
+        }
+        currentRepeatIteration = 0;
+        lastRepeatTime = UNRESOLVED;
+        isActive = false;
+        isFrozen = false;
+        lastSampleTime = UNRESOLVED;
+        // XXX should reconvert resolved syncbase/wallclock/media-marker time
+        //     instances into the parent simple timespace
+    }
+
+    /**
+     * Parses the animation attributes for this timed element.
+     */
+    public void parseAttributes(String begin, String dur, String end,
+                                String min, String max, String repeatCount,
+                                String repeatDur, String fill,
+                                String restart) {
+        if (!hasParsed) {
+            parseBegin(begin);
+            parseDur(dur);
+            parseEnd(end);
+            parseMin(min);
+            parseMax(max);
+            if (this.min > this.max) {
+                this.min = 0f;
+                this.max = INDEFINITE;
+            }
+            parseRepeatCount(repeatCount);
+            parseRepeatDur(repeatDur);
+            parseFill(fill);
+            parseRestart(restart);
+            hasParsed = true;
+        }
+    }
+
+    /**
+     * Parses a new 'begin' attribute.
+     */
+    protected void parseBegin(String begin) {
+        try {
+            if (begin.length() == 0) {
+                begin = SMIL_BEGIN_DEFAULT_VALUE;
+            }
+            beginTimes = TimingSpecifierListProducer.parseTimingSpecifierList
+                (TimedElement.this, true, begin,
+                 root.useSVG11AccessKeys, root.useSVG12AccessKeys);
+        } catch (ParseException ex) {
+            throw createException
+                ("attribute.malformed",
+                 new Object[] { null, SMIL_BEGIN_ATTRIBUTE });
+        }
+    }
+
+    /**
+     * Parses a new 'dur' attribute.
+     */
+    protected void parseDur(String dur) {
+        if (dur.equals(SMIL_MEDIA_VALUE)) {
+            durMedia = true;
+            simpleDur = UNRESOLVED;
+        } else {
+            durMedia = false;
+            if (dur.length() == 0 || dur.equals(SMIL_INDEFINITE_VALUE)) {
+                simpleDur = INDEFINITE;
+            } else {
+                try {
+                    simpleDur = parseClockValue(dur, false);
+                } catch (ParseException e) {
+                    throw createException
+                        ("attribute.malformed",
+                         new Object[] { null, SMIL_DUR_ATTRIBUTE });
+                }
+                if (simpleDur < 0) {
+                    simpleDur = INDEFINITE;
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses a clock value or offset and returns it as a float.
+     */
+    protected float parseClockValue(String s, boolean parseOffset)
+            throws ParseException {
+        ClockParser p = new ClockParser(parseOffset);
+        class Handler implements ClockHandler {
+            protected float v = 0;
+            public void clockValue(float newClockValue) {
+                v = newClockValue;
+            }
+        }
+
+        Handler h = new Handler();
+        p.setClockHandler(h);
+        p.parse(s);
+        return h.v;
+    }
+
+    /**
+     * Parses a new 'end' attribute.
+     */
+    protected void parseEnd(String end) {
+        try {
+            endTimes = TimingSpecifierListProducer.parseTimingSpecifierList
+                (TimedElement.this, false, end,
+                 root.useSVG11AccessKeys, root.useSVG12AccessKeys);
+        } catch (ParseException ex) {
+            throw createException
+                ("attribute.malformed",
+                 new Object[] { null, SMIL_END_ATTRIBUTE });
+        }
+    }
+
+    /**
+     * Parses a new 'min' attribute.
+     */
+    protected void parseMin(String min) {
+        if (min.equals(SMIL_MEDIA_VALUE)) {
+            this.min = 0;
+            minMedia = true;
+        } else {
+            minMedia = false;
+            if (min.length() == 0) {
+                this.min = 0;
+            } else {
+                try {
+                    this.min = parseClockValue(min, false);
+                } catch (ParseException ex) {
+                	this.min = 0;
+                }
+                if (this.min < 0) {
+                    this.min = 0;
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses a new 'max' attribute.
+     */
+    protected void parseMax(String max) {
+        if (max.equals(SMIL_MEDIA_VALUE)) {
+            this.max = INDEFINITE;
+            maxMedia = true;
+        } else {
+            maxMedia = false;
+            if (max.length() == 0 || max.equals(SMIL_INDEFINITE_VALUE)) {
+                this.max = INDEFINITE;
+            } else {
+                try {
+                    this.max = parseClockValue(max, false);
+                } catch (ParseException ex) {
+                	this.max = INDEFINITE;
+                }
+                if (this.max < 0) {
+                    this.max = 0;
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses a new 'repeatCount' attribute.
+     */
+    protected void parseRepeatCount(String repeatCount) {
+        if (repeatCount.length() == 0) {
+            this.repeatCount = UNRESOLVED;
+        } else if (repeatCount.equals(SMIL_INDEFINITE_VALUE)) {
+            this.repeatCount = INDEFINITE;
+        } else {
+            try {
+                this.repeatCount = Float.parseFloat(repeatCount);
+                if (this.repeatCount > 0) {
+                    return;
+                }
+            } catch (NumberFormatException ex) {
+                throw createException
+                    ("attribute.malformed",
+                     new Object[] { null, SMIL_REPEAT_COUNT_ATTRIBUTE });
+            }
+        }
+    }
+
+    /**
+     * Parses a new 'repeatDur' attribute.
+     */
+    protected void parseRepeatDur(String repeatDur) {
+        try {
+            if (repeatDur.length() == 0) {
+                this.repeatDur = UNRESOLVED;
+            } else if (repeatDur.equals(SMIL_INDEFINITE_VALUE)) {
+                this.repeatDur = INDEFINITE;
+            } else {
+                this.repeatDur = parseClockValue(repeatDur, false);
+            }
+        } catch (ParseException ex) {
+            throw createException
+                ("attribute.malformed",
+                 new Object[] { null, SMIL_REPEAT_DUR_ATTRIBUTE });
+        }
+    }
+
+    /**
+     * Parses a new 'fill' attribute.
+     */
+    protected void parseFill(String fill) {
+        if (fill.length() == 0 || fill.equals(SMIL_REMOVE_VALUE)) {
+            fillMode = FILL_REMOVE;
+        } else if (fill.equals(SMIL_FREEZE_VALUE)) {
+            fillMode = FILL_FREEZE;
+        } else {
+            throw createException
+                ("attribute.malformed",
+                 new Object[] { null, SMIL_FILL_ATTRIBUTE });
+        }
+    }
+
+    /**
+     * Parses a new 'restart' attribute.
+     */
+    protected void parseRestart(String restart) {
+        if (restart.length() == 0 || restart.equals(SMIL_ALWAYS_VALUE)) {
+            restartMode = RESTART_ALWAYS;
+        } else if (restart.equals(SMIL_WHEN_NOT_ACTIVE_VALUE)) {
+            restartMode = RESTART_WHEN_NOT_ACTIVE;
+        } else if (restart.equals(SMIL_NEVER_VALUE)) {
+            restartMode = RESTART_NEVER;
+        } else {
+            throw createException
+                ("attribute.malformed",
+                 new Object[] { null, SMIL_RESTART_ATTRIBUTE });
+        }
+    }
+
+    /**
+     * Initializes this timed element.
+     */
+    public void initialize() {
+        for (int i = 0; i < beginTimes.length; i++) {
+            beginTimes[i].initialize();
+        }
+        for (int i = 0; i < endTimes.length; i++) {
+            endTimes[i].initialize();
+        }
+    }
+
+    /**
+     * Deinitializes this timed element.
+     */
+    public void deinitialize() {
+        for (int i = 0; i < beginTimes.length; i++) {
+            beginTimes[i].deinitialize();
+        }
+        for (int i = 0; i < endTimes.length; i++) {
+            endTimes[i].deinitialize();
+        }
+    }
+
+    /**
+     * Adds a time to the begin time instance list that will cause
+     * the element to begin immediately (if restart semantics allow it).
+     */
+    public void beginElement() {
+        beginElement(0);
+    }
+
+    /**
+     * Adds a time to the begin time instance list that will cause
+     * the element to begin at some offset to the current time (if restart
+     * semantics allow it).
+     */
+    public void beginElement(float offset) {
+        float t = root.convertWallclockTime( Calendar.getInstance());
+        InstanceTime it = new InstanceTime(null, t + offset, true);
+        addInstanceTime(it, true);
+    }
+
+    /**
+     * Adds a time to the end time instance list that will cause
+     * the element to end immediately (if restart semantics allow it).
+     */
+    public void endElement() {
+        endElement(0);
+    }
+
+    /**
+     * Adds a time to the end time instance list that will cause
+     * the element to end at some offset to the current time (if restart
+     * semantics allow it).
+     */
+    public void endElement(float offset) {
+        float t = root.convertWallclockTime(Calendar.getInstance());
+        InstanceTime it = new InstanceTime(null, t + offset, true);
+        addInstanceTime(it, false);
+    }
+
+    /**
+     * Returns the last sample time of this element, in local active time.
+     */
+    public float getLastSampleTime() {
+        return lastSampleTime;
+    }
+
+    /**
+     * Returns the begin time of the current interval, in parent simple time,
+     * or <code>Float.NaN</code> if the element is not active.
+     */
+    public float getCurrentBeginTime() {
+        float begin;
+        if (currentInterval == null
+                || (begin = currentInterval.getBegin()) < lastSampleTime) {
+            return Float.NaN;
+        }
+        return begin;
+    }
+
+    /**
+     * Returns whether this element can be begun or restarted currently.
+     */
+    public boolean canBegin() {
+        return currentInterval == null
+            || isActive && restartMode != RESTART_NEVER;
+    }
+
+    /**
+     * Returns whether this element can be ended currently.
+     */
+    public boolean canEnd() {
+        return isActive;
+    }
+
+    /**
+     * Returns the time that the document would seek to if this animation
+     * element were hyperlinked to, or <code>NaN</code> if there is no
+     * such begin time.
+     */
+    public float getHyperlinkBeginTime() {
+        if (isActive) {
+            return currentInterval.getBegin();
+        }
+        if (!beginInstanceTimes.isEmpty()) {
+            return ((InstanceTime) beginInstanceTimes.get(0)).getTime();
+        }
+        return Float.NaN;
+    }
+
+    /**
+     * Fires a TimeEvent of the given type on this element.
+     * @param eventType the type of TimeEvent ("beginEvent", "endEvent"
+     *                  or "repeatEvent").
+     * @param time the timestamp of the event object
+     * @param detail the repeat iteration, if this event is a repeat event
+     */
+    protected void fireTimeEvent(String eventType, float time, int detail) {
+        Calendar t = (Calendar) root.getDocumentBeginTime().clone();
+        t.add(Calendar.MILLISECOND, (int) Math.round(time * 1e3));
+        fireTimeEvent(eventType, t, detail);
+    }
+
+    /**
+     * Invoked by a {@link TimingSpecifier} to indicate that an event occurred
+     * that would create a new instance time for this timed element.  These
+     * will be processed at the beginning of the next tick.
+     */
+    void eventOccurred(TimingSpecifier t, Event e) {
+        Set ts = (HashSet) handledEvents.get(e);
+        if (ts == null) {
+            ts = new HashSet();
+            handledEvents.put(e, ts);
+        }
+        ts.add(t);
+        root.currentIntervalWillUpdate();
+    }
+
+    /**
+     * Fires a TimeEvent of the given type on this element.
+     * @param eventType the type of TimeEvent ("beginEvent", "endEvent"
+     *                  or "repeatEvent").
+     * @param time the timestamp of the event object
+     */
+    protected abstract void fireTimeEvent(String eventType, Calendar time,
+                                          int detail);
+
+    /**
+     * Invoked to indicate this timed element became active at the
+     * specified time.
+     * @param begin the time the element became active, in document simple time
+     */
+    protected abstract void toActive(float begin);
+
+    /**
+     * Invoked to indicate that this timed element became inactive.
+     * @param stillActive if true, indicates that the element is still actually
+     *                    active, but between the end of the computed repeat
+     *                    duration and the end of the interval
+     * @param isFrozen whether the element is frozen or not
+     */
+    protected abstract void toInactive(boolean stillActive, boolean isFrozen);
+
+    /**
+     * Invoked to indicate that this timed element has had its fill removed.
+     */
+    protected abstract void removeFill();
+
+    /**
+     * Invoked to indicate that this timed element has been sampled at the
+     * given time.
+     * @param simpleTime the sample time in local simple time
+     * @param simpleDur the simple duration of the element
+     * @param repeatIteration the repeat iteration during which the element
+     *                        was sampled
+     */
+    protected abstract void sampledAt(float simpleTime, float simpleDur,
+                                      int repeatIteration);
+
+    /**
+     * Invoked to indicate that this timed element has been sampled
+     * at the end of its active time, at an integer multiple of the
+     * simple duration.  This is the "last" value that will be used
+     * for filling, which cannot be sampled normally.
+     */
+    protected abstract void sampledLastValue(int repeatIteration);
+
+    /**
+     * Returns the timed element with the given ID.
+     */
+    protected abstract TimedElement getTimedElementById(String id);
+
+    /**
+     * Returns the event target with the given ID.
+     */
+    protected abstract EventTarget getEventTargetById(String id);
+
+    /**
+     * Returns the event target that should be listened to for
+     * access key events.
+     */
+    protected abstract EventTarget getRootEventTarget();
+
+    /**
+     * Returns the DOM element that corresponds to this timed element, if
+     * such a DOM element exists.
+     */
+    public abstract Element getElement();
+
+    /**
+     * Returns the target of this animation as an {@link EventTarget}.  Used
+     * for eventbase timing specifiers where the element ID is omitted.
+     */
+    protected abstract EventTarget getAnimationEventTarget();
+
+    /**
+     * Returns whether this timed element comes before the given timed element
+     * in document order.
+     */
+    public abstract boolean isBefore(TimedElement other);
+
+    /**
+     * Returns whether this timed element is for a constant animation (i.e., a
+     * 'set' animation.
+     */
+    protected abstract boolean isConstantAnimation();
+
+    /**
+     * Creates and returns a new {@link AnimationException}.
+     */
+    public AnimationException createException(String code, Object[] params) {
+        Element e = getElement();
+        if (e != null) {
+            params[0] = e.getNodeName();
+        }
+        return new AnimationException(this, code, params);
+    }
+
+    /**
+     * The error messages bundle class name.
+     */
+    protected static final String RESOURCES =
+        "org.apache.flex.forks.batik.anim.resources.Messages";
+
+    /**
+     * The localizable support for the error messages.
+     */
+    protected static LocalizableSupport localizableSupport =
+        new LocalizableSupport(RESOURCES, TimedElement.class.getClassLoader());
+
+    /**
+     * Implements {@link org.apache.flex.forks.batik.i18n.Localizable#setLocale(java.util.Locale)}.
+     */
+    public static void setLocale( Locale l) {
+        localizableSupport.setLocale(l);
+    }
+
+    /**
+     * Implements {@link org.apache.flex.forks.batik.i18n.Localizable#getLocale()}.
+     */
+    public static Locale getLocale() {
+        return localizableSupport.getLocale();
+    }
+
+    /**
+     * Implements {@link
+     * org.apache.flex.forks.batik.i18n.Localizable#formatMessage(String,Object[])}.
+     */
+    public static String formatMessage(String key, Object[] args)
+        throws MissingResourceException {
+        return localizableSupport.formatMessage(key, args);
+    }
+
+    /**
+     * Returns a string representation of the given time value.
+     */
+    public static String toString(float time) {
+        if (Float.isNaN(time)) {
+            return "UNRESOLVED";
+        } else if (time == Float.POSITIVE_INFINITY) {
+            return "INDEFINITE";
+        } else {
+            return Float.toString(time);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/f690ea2f/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphAdapter.java
----------------------------------------------------------------------
diff --git a/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphAdapter.java b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphAdapter.java
new file mode 100644
index 0000000..762b1dd
--- /dev/null
+++ b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphAdapter.java
@@ -0,0 +1,106 @@
+/*
+
+   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.
+
+ */
+package org.apache.flex.forks.batik.anim.timing;
+
+/**
+ * An adapter class for {@link TimegraphListener}s.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id: TimegraphAdapter.java 475477 2006-11-15 22:44:28Z cam $
+ */
+public class TimegraphAdapter implements TimegraphListener {
+
+    /**
+     * Invoked to indicate that a timed element has been added to the
+     * document.
+     */
+    public void elementAdded(TimedElement e) {
+    }
+
+    /**
+     * Invoked to indicate that a timed element has been removed from the
+     * document.
+     */
+    public void elementRemoved(TimedElement e) {
+    }
+
+    /**
+     * Invoked to indicate that a timed element has become active.
+     * @param e the TimedElement that became active
+     * @param t the time (in parent simple time) that the element became active
+     */
+    public void elementActivated(TimedElement e, float t) {
+    }
+
+    /**
+     * Invoked to indicate that a timed element has become inactive
+     * and is filling.
+     */
+    public void elementFilled(TimedElement e, float t) {
+    }
+
+    /**
+     * Invoked to indicate that a timed element has become inactive
+     * and is not filling.
+     */
+    public void elementDeactivated(TimedElement e, float t) {
+    }
+
+    /**
+     * Invoked to indivate that an interval was created for the given
+     * timed element.
+     */
+    public void intervalCreated(TimedElement e, Interval i) {
+    }
+
+    /**
+     * Invoked to indivate that an interval was removed for the given
+     * timed element.
+     */
+    public void intervalRemoved(TimedElement e, Interval i) {
+    }
+
+    /**
+     * Invoked to indivate that an interval's endpoints were changed.
+     */
+    public void intervalChanged(TimedElement e, Interval i) {
+    }
+
+    /**
+     * Invoked to indivate that the given interval began.
+     * @param i the Interval that began, or null if no interval is
+     *          active for the given timed element.
+     */
+    public void intervalBegan(TimedElement e, Interval i) {
+    }
+
+    /**
+     * Invoked to indicate that the given timed element began a repeat
+     * iteration at the specified time.
+     */
+    public void elementRepeated(TimedElement e, int i, float t) {
+    }
+
+    /**
+     * Invoked to indicate that the list of instance times for the given
+     * timed element has been updated.
+     */
+    public void elementInstanceTimesChanged(TimedElement e, float isBegin) {
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/f690ea2f/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphListener.java
----------------------------------------------------------------------
diff --git a/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphListener.java b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphListener.java
new file mode 100644
index 0000000..68a9b44
--- /dev/null
+++ b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimegraphListener.java
@@ -0,0 +1,95 @@
+/*
+
+   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.
+
+ */
+package org.apache.flex.forks.batik.anim.timing;
+
+/**
+ * An interface for listening to timing events in a timed document.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id: TimegraphListener.java 475477 2006-11-15 22:44:28Z cam $
+ */
+public interface TimegraphListener {
+
+    /**
+     * Invoked to indicate that a timed element has been added to the
+     * document.
+     */
+    void elementAdded(TimedElement e);
+
+    /**
+     * Invoked to indicate that a timed element has been removed from the
+     * document.
+     */
+    void elementRemoved(TimedElement e);
+
+    /**
+     * Invoked to indicate that a timed element has become active.
+     * @param e the TimedElement that became active
+     * @param t the time (in parent simple time) that the element became active
+     */
+    void elementActivated(TimedElement e, float t);
+
+    /**
+     * Invoked to indicate that a timed element has become inactive
+     * and is filling.
+     */
+    void elementFilled(TimedElement e, float t);
+
+    /**
+     * Invoked to indicate that a timed element has become inactive
+     * and is not filling.
+     */
+    void elementDeactivated(TimedElement e, float t);
+
+    /**
+     * Invoked to indivate that an interval was created for the given
+     * timed element.
+     */
+    void intervalCreated(TimedElement e, Interval i);
+
+    /**
+     * Invoked to indivate that an interval was removed for the given
+     * timed element.
+     */
+    void intervalRemoved(TimedElement e, Interval i);
+
+    /**
+     * Invoked to indivate that an interval's endpoints were changed.
+     */
+    void intervalChanged(TimedElement e, Interval i);
+
+    /**
+     * Invoked to indivate that the given interval began.
+     * @param i the Interval that began, or null if no interval is
+     *          active for the given timed element.
+     */
+    void intervalBegan(TimedElement e, Interval i);
+
+    /**
+     * Invoked to indicate that the given timed element began a repeat
+     * iteration at the specified time.
+     */
+    void elementRepeated(TimedElement e, int i, float t);
+
+    /**
+     * Invoked to indicate that the list of instance times for the given
+     * timed element has been updated.
+     */
+    void elementInstanceTimesChanged(TimedElement e, float isBegin);
+}

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/f690ea2f/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifier.java
----------------------------------------------------------------------
diff --git a/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifier.java b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifier.java
new file mode 100644
index 0000000..e96d7a2
--- /dev/null
+++ b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifier.java
@@ -0,0 +1,110 @@
+/*
+
+   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.
+
+ */
+package org.apache.flex.forks.batik.anim.timing;
+
+/**
+ * An abstract class for SMIL timing specifiers.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id: TimingSpecifier.java 485485 2006-12-11 04:04:53Z cam $
+ */
+public abstract class TimingSpecifier {
+
+    /**
+     * The element that owns this timing specifier.
+     */
+    protected TimedElement owner;
+
+    /**
+     * Whether this timing specifier is for a begin time or an end time.
+     */
+    protected boolean isBegin;
+
+    /**
+     * Creates a new TimingSpecifier object.
+     */
+    protected TimingSpecifier(TimedElement owner, boolean isBegin) {
+        this.owner = owner;
+        this.isBegin = isBegin;
+    }
+
+    /**
+     * Returns the element that owns this timing specifier.
+     */
+    public TimedElement getOwner() {
+        return owner;
+    }
+
+    /**
+     * Returns true if this timing specifier is in the owner's begin list,
+     * false if it is in the owner's end list.
+     */
+    public boolean isBegin() {
+        return isBegin;
+    }
+
+    /**
+     * Initializes this timing specifier by adding the initial instance time
+     * to the owner's instance time list or setting up any event listeners.
+     * This should be overriden in descendant classes.
+     */
+    public void initialize() {
+    }
+
+    /**
+     * Deinitializes this timing specifier by removing any event listeners.
+     * This should be overriden in descendant classes.
+     */
+    public void deinitialize() {
+    }
+
+    /**
+     * Returns whether this timing specifier is event-like (i.e., if it is
+     * an eventbase, accesskey or a repeat timing specifier).
+     */
+    public abstract boolean isEventCondition();
+
+    /**
+     * Called by the timebase element when it creates a new Interval.
+     * This should be overridden in descendant classes that generate
+     * time instances based on the interval of a timebase element.
+     */
+    float newInterval(Interval interval) {
+        return Float.POSITIVE_INFINITY;
+    }
+
+    /**
+     * Called by the timebase element when it deletes an Interval.
+     * This should be overridden in descendant classes that generate
+     * time instances based on the interval of a timebase element.
+     */
+    float removeInterval(Interval interval) {
+        return Float.POSITIVE_INFINITY;
+    }
+
+    /**
+     * Called by an {@link InstanceTime} created by this TimingSpecifier
+     * to indicate that its value has changed.  This should be overriden
+     * in descendant classes that generate time instances based on the
+     * interval of a timebase element.
+     */
+    float handleTimebaseUpdate(InstanceTime instanceTime, float newTime) {
+        return Float.POSITIVE_INFINITY;
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/f690ea2f/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifierListProducer.java
----------------------------------------------------------------------
diff --git a/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifierListProducer.java b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifierListProducer.java
new file mode 100644
index 0000000..6513762
--- /dev/null
+++ b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/TimingSpecifierListProducer.java
@@ -0,0 +1,177 @@
+/*
+
+   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.
+
+ */
+package org.apache.flex.forks.batik.anim.timing;
+
+import java.util.Calendar;
+import java.util.LinkedList;
+
+import org.apache.flex.forks.batik.parser.DefaultTimingSpecifierListHandler;
+import org.apache.flex.forks.batik.parser.TimingSpecifierListParser;
+
+/**
+ * A {@link org.apache.flex.forks.batik.parser.TimingSpecifierListHandler} that creates
+ * {@link TimingSpecifier}s.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id: TimingSpecifierListProducer.java 475477 2006-11-15 22:44:28Z cam $
+ */
+public class TimingSpecifierListProducer
+        extends DefaultTimingSpecifierListHandler {
+
+    /**
+     * The list of parsed timing specifiers.
+     */
+    protected LinkedList timingSpecifiers = new LinkedList();
+
+    /**
+     * The owner TimedElement used when creating the TimingSpecifiers.
+     */
+    protected TimedElement owner;
+
+    /**
+     * Whether the created TimingSpecifiers should be begin times.
+     */
+    protected boolean isBegin;
+
+    /**
+     * Creates a new TimingSpecifierListProducer.
+     */
+    public TimingSpecifierListProducer(TimedElement owner, boolean isBegin) {
+        this.owner = owner;
+        this.isBegin = isBegin;
+    }
+
+    /**
+     * Returns an array of the parsed TimingSpecifiers.
+     */
+    public TimingSpecifier[] getTimingSpecifiers() {
+        return (TimingSpecifier[]) timingSpecifiers.toArray(new TimingSpecifier[0]);
+    }
+
+    /**
+     * Parses a timing specifier list.
+     */
+    public static TimingSpecifier[] parseTimingSpecifierList
+            (TimedElement owner, boolean isBegin, String spec,
+             boolean useSVG11AccessKeys, boolean useSVG12AccessKeys) {
+        TimingSpecifierListParser p =
+            new TimingSpecifierListParser(useSVG11AccessKeys,
+                                          useSVG12AccessKeys);
+        TimingSpecifierListProducer pp =
+            new TimingSpecifierListProducer(owner, isBegin);
+        p.setTimingSpecifierListHandler(pp);
+        p.parse(spec);
+        TimingSpecifier[] specs = pp.getTimingSpecifiers();
+        return specs;
+    }
+
+    // TimingSpecifierHandler ////////////////////////////////////////////////
+
+    /**
+     * Invoked when an offset value timing specifier is parsed.
+     */
+    public void offset(float offset) {
+        TimingSpecifier ts = new OffsetTimingSpecifier(owner, isBegin, offset);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when a syncbase value timing specifier is parsed.
+     */
+    public void syncbase(float offset, String syncbaseID,
+                         String timeSymbol) {
+        TimingSpecifier ts = new SyncbaseTimingSpecifier
+            (owner, isBegin, offset, syncbaseID, timeSymbol.charAt(0) == 'b');
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when an eventbase value timing specifier is parsed.
+     */
+    public void eventbase(float offset, String eventbaseID,
+                          String eventType) {
+        TimingSpecifier ts = new EventbaseTimingSpecifier
+            (owner, isBegin, offset, eventbaseID, eventType);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when a repeat value timing specifier with no iteration
+     * is parsed.
+     */
+    public void repeat(float offset, String syncbaseID) {
+        TimingSpecifier ts = new RepeatTimingSpecifier
+            (owner, isBegin, offset, syncbaseID);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when a repeat value timing specifier with an iteration
+     * is parsed.
+     */
+    public void repeat(float offset, String syncbaseID,
+                       int repeatIteration) {
+        TimingSpecifier ts = new RepeatTimingSpecifier
+            (owner, isBegin, offset, syncbaseID, repeatIteration);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when an accesskey value timing specifier is parsed.
+     */
+    public void accesskey(float offset, char key) {
+        TimingSpecifier ts = new AccesskeyTimingSpecifier
+            (owner, isBegin, offset, key);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when an SVG 1.2 accessKey value timing specifier is parsed.
+     */
+    public void accessKeySVG12(float offset, String keyName) {
+        TimingSpecifier ts = new AccesskeyTimingSpecifier
+            (owner, isBegin, offset, keyName);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when a media marker value timing specifier is parsed.
+     */
+    public void mediaMarker(String syncbaseID, String markerName) {
+        TimingSpecifier ts = new MediaMarkerTimingSpecifier
+            (owner, isBegin, syncbaseID, markerName);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when a wallclock value timing specifier is parsed.
+     */
+    public void wallclock(Calendar time) {
+        TimingSpecifier ts = new WallclockTimingSpecifier(owner, isBegin, time);
+        timingSpecifiers.add(ts);
+    }
+
+    /**
+     * Invoked when an indefinite value timing specifier is parsed.
+     */
+    public void indefinite() {
+        TimingSpecifier ts = new IndefiniteTimingSpecifier(owner, isBegin);
+        timingSpecifiers.add(ts);
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/f690ea2f/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/Trace.java
----------------------------------------------------------------------
diff --git a/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/Trace.java b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/Trace.java
new file mode 100644
index 0000000..c4e2f4c
--- /dev/null
+++ b/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/anim/timing/Trace.java
@@ -0,0 +1,68 @@
+/*
+
+   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.
+
+ */
+package org.apache.flex.forks.batik.anim.timing;
+
+/**
+ * Animation debugging support.  To be removed.
+ *
+ * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
+ * @version $Id: Trace.java 475477 2006-11-15 22:44:28Z cam $
+ */
+public class Trace {
+
+    private static int level;
+
+    private static boolean enabled = false;
+
+    public static void enter(Object o, String fn, Object[] args) {
+        if (enabled) {
+            System.err.print("LOG\t");
+            for (int i = 0; i < level; i++) {
+                System.err.print("  ");
+            }
+            if (fn == null) {
+                System.err.print("new " + o.getClass().getName() + "(");
+            } else {
+                System.err.print(o + "." + fn + "(");
+            }
+            if (args != null) {
+                System.err.print(args[0]);
+                for (int i = 1; i < args.length; i++) {
+                    System.err.print(", " + args[i]);
+                }
+            }
+            System.err.println(")");
+        }
+            level++;
+    }
+    
+    public static void exit() {
+        level--;
+    }
+
+    public static void print(String s) {
+        if (enabled) {
+            System.err.print("LOG\t");
+            for (int i = 0; i < level; i++) {
+                System.err.print("  ");
+            }
+            System.err.println(s);
+        }
+    }
+}