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

TimedElementSupport

public class TimedElementSupport extends Object
The TimedElementSupport class is the main abstraction of the SMIL timing model implementation.

It is responsible for computing the animation's state (inactive, active, post-acitve, frozen) and, when the application is active, what the current simple time is. That simple time is then used by the animation engine to compute animated values.

SMIL has a notion of time containers. A TimedElementSupport is the child of a time container. The relationship is that the element's current time is the container's simple time. In otherwords, sampling a container for a given time (in its own container), yields a simple time for the container. That simple time is the timed element's current time, used to compute the timed element's own simple time. Refer to the SMIL 2 specification for an explanation of what the various time semantics are.

version
$Id: TimedElementSupport.java,v 1.7 2006/07/13 00:55:58 st125089 Exp $

Fields Summary
public static final String
LAST_DUR_END_EVENT_TYPE
Last Simple Duration End event type.
public static final String
SEEK_END_EVENT_TYPE
Seek End event type.
public static final String
SEEK_BEGIN_EVENT_TYPE
Seek End event type.
public static final String
END_EVENT_TYPE
End event type.
public static final String
BEGIN_EVENT_TYPE
Begin event type.
public static final String
REPEAT_EVENT_TYPE
Repeat event type
public static final int
RESTART_ALWAYS
Used when this TimedElement can be restarted under any circumstance.
public static final int
RESTART_WHEN_NOT_ACTIVE
Used when this TimedElement can only be restarted when it is not active (i.e., it does not have a current interval.
public static final int
RESTART_NEVER
Used when this TimedElement cannot be restarted under any circumstance
public static final int
FILL_BEHAVIOR_REMOVE
Used when this TimedElement should not modify the animated value when it is post active.
public static final int
FILL_BEHAVIOR_FREEZE
Used when this TimedElement should maintain the last value of its simple interval after it becomes inactive.
protected static final int
STATE_PRE_INIT
State before the element has been initialized
protected static final int
STATE_NO_INTERVAL
State where there has never been a current interval (since the last init).
protected static final int
STATE_WAITING_INTERVAL_0
State while the element is waiting to begin the first interval.
protected static final int
STATE_WAITING_INTERVAL_N
State while the element is waiting to begin an interval other than the first one
protected static final int
STATE_PLAYING
State while the element is playing the current interval.
protected static final int
STATE_FILL
State when the element performs any fill on the previous interval.
protected final Time
eventTime
This time value is used to avoid creating Time instances over and over again in animation loops, when dispatching events.
int
state
The element's state. One of the STATE_XXX constants.
boolean
playFill
Controls whether the element is in this rare state where it is playing but its last simple duration is finished. In that state, the element is 'playing', but acting as if frozen. There are situations where the min attribute may cause the active duration to go beyond the time of the last simple duration instance. In that case, the element is still in the playing state, but we mark it as 'playFill', which means that it should behave almost as if in the fill state.
int
curIter
The current iteration (only used while playing
Time
dur
This element's simple duration. This corresponds to the 'dur' attribute. Note that this is only one of the parameters that defines the actual simple duration.
float
repeatCount
The number of times this element repeats the simple duration. NaN means unspecified.
Time
repeatDur
repeatDur set a limit on active duration length.
Time
implicitDuration
This element's implicit duration
Time
min
min sets a lower limit on the active duration.
Time
max
max sets an upper limit on the active duration
int
restart
Defines the restart behavior for this element. This should be one of RESTART_ALWAYS, RESTART_WHEN_NOT_ACTIVE, RESTART_NEVER.
int
fillBehavior
Defines the behavior after this element is no longer active. If FILL_BEHAVIOR_FILL, it means the element will keep sampling at the last simple duration time. If FILL_BEHAVIOR_REMOVE (the default), the effect of the animation is removed when the element is inactive (i.e., when it no longer has a current interval.
protected TimeContainerSupport
timeContainer
A reference to this element's time container. This is initialized anytime the parent is set. This element's current time is the container's simple time.
TimeInterval
currentInterval
Keeps a reference to the current TimeInterval
long
lastSampleTime
Last local time (i.e., within the simple duration) this element was sampled at.
Time
simpleDur
The simple duration for the current interval
TimeInterval
previousInterval
Keeps a reference to the interval preceding the current one.
Vector
beginInstances
List of begin TimeInstances.
Vector
endInstances
List of end TimeInstances.
Vector
beginConditions
Begin TimeConditions. Should _never_ be null.
Vector
endConditions
End TimeConditions. Should _never_ be null.
Vector
beginDependents
List of SyncBaseConditions depedent on this element's begin condition.
Vector
endDependents
List of SyncBaseConditions dependent on this element's end condition.
ModelNode
animationElement
The associated animation element.
boolean
timingUpdate
Time dependency cycles detector. This flag is set to true when a timing update starts. There should only be one at a time, and it should always be set back to true after and timing update is complete.
boolean
seeking
The seeking flag is used when seeking to a particular time to prevent generation of beginEvent, endEvent and repeat event.
Constructors Summary
Methods Summary
public voidactivate()
Called when this element is the target of an hyperlink. The behavior is that defined in the SMIL 2 specification.

see
SMIL 2 Specification

        // 1. If the target element is active, seek the document time back to
        // the begin time of the current interval for the element.
        if (state == STATE_PLAYING) {
            Time seekToTime = new Time(currentInterval.begin.value);
            seekToTime = toRootContainerSimpleTimeClamp(seekToTime);

            if (seekToTime.isResolved()) {
                seekTo(seekToTime);
                return;
            }
        }

        //
        // 2. Else if the target element begin time is resolved (i.e. there is
        // at least one interval defined for the element), seek the document
        // time (forward or back, as needed) to the begin time of the first
        // interval for the target element. Note that the begin time may be
        // resolved as a result of an earlier hyperlink, DOM or event
        // activation. Once the begin time is resolved (and until the element is
        // reset, e.g. when the parent repeats), hyperlink traversal always
        // seeks. For a discussion of "reset", see Resetting element state. Note
        // also that for an element begin to be resolved, the begin time of all
        // ancestor elements must also be resolved.
        //
        TimeInterval firstInterval = computeFirstInterval();
        if (firstInterval != null) {
            Time seekToTime = new Time(firstInterval.begin.value);

            seekToTime = toRootContainerSimpleTimeClamp(seekToTime);

            if (seekToTime.isResolved()) {
                seekTo(seekToTime);
                return;
            }
        }

        // 3. Else (i.e. there are no defined intervals for the element), the
        // target element begin time must be resolved. This may require seeking
        // and/or resolving ancestor elements as well. This is done by recursing
        // from the target element up to the closest ancestor element that has a
        // resolved begin time (again noting that for an element to have a
        // resolved begin time, all of its ancestors must have resolved begin
        // times). Then, the recursion is "unwound", and for each ancestor in
        // turn (beneath the resolved ancestor) as well as the target element,
        // the following steps are performed:
        //
        //     1. If the element begin time is resolved, seek the document time
        //     (forward or back, as needed) to the begin time of the first
        //     interval for the target element.
        //
        //     2. Else (if the begin time is not resolved), just resolve the
        //     element begin time at the current time on its parent time
        //     container (given the current document position). Disregard the
        //     sync-base or event base of the element, and do not
        //     "back-propagate" any timing logic to resolve the element, but
        //     rather treat it as though it were defined with begin="indefinite"
        //     and just resolve begin time to the current parent time. This
        //     should create an interval and propagate to time dependents.
        //
        seekTo(Time.UNRESOLVED);
    
voidaddInstance(boolean isBegin, long offset)
Adds a new TimedInstance with the specified offset to the begin or end instance list (depending on isBegin

param
isBegin if true, the instance is a begin instance
param
offset the instance offset from 0.

        Time currentTime = getCurrentTime();
        if (!currentTime.isResolved()) {
            // If the current time is not resolved, it means the time
            // container is not active. Therefore, there is no way
            // we can add a new time instance that would be meaningful. 
            // So we do nothing.
            return;
        }

        Time newTime = new Time(currentTime.value + offset);

        TimeInstance newInstance 
            = new TimeInstance(this, newTime, true, isBegin);
    
voidaddTimeCondition(TimeCondition newCondition)
Called by TimeConditions when they are constructed.

param
newCondition the new TimeCondition to add to the list. Should not be null.

        if (newCondition.isBegin) {
            beginConditions.addElement(newCondition);
        } else {
            endConditions.addElement(newCondition);
        }
    
voidaddTimeInstance(TimeInstance newInstance)
Called to add a new TimeInstance to the begin or end instance list (depending on the new instance's isBegin setting). This is also called as a result of invoking beginAt or endAt.

param
newInstance the new TimeInstance to add to the list. Should not be null.
see
#beginAt
see
#endAt
see
SMIL 2, Interaction with restart semantic

        if (timingUpdate) {
            // We are in a timing dependency cycle. Break now.
            return;
        }
        
        timingUpdate = true;

        try {
            insertTimeInstance(newInstance);
            
            // Do not handle instance time list changes if we are 
            // initializing the time graph.
            if (state == STATE_PRE_INIT) {
                return;
            }
            
            if (!newInstance.isBegin) {
                reEvaluateEndTime();
                return;
            }
            
            // Impact the current interval if needed
            switch (state) {
            case STATE_NO_INTERVAL:
                checkNewInterval(computeFirstInterval());
                break;
            case STATE_FILL:
                if (restart != RESTART_NEVER) {
                    checkNewInterval(computeNextInterval());
                } 
                // Else, don't do anything: this element cannot
                // restart.
                break;
            case STATE_WAITING_INTERVAL_0:
                {
                    TimeInterval curInt = computeFirstInterval();
                    if (curInt != null) {
                        currentInterval.setBegin(curInt.begin);
                        currentInterval.setEnd(curInt.end);
                        computeLastDur(currentInterval);
                    }
                }
                break;
            case STATE_WAITING_INTERVAL_N:
                {
                    TimeInterval curInt = computeNextInterval();
                    // We just added a new time instance. It is not possible
                    // that adding a time instance would make 
                    // computeNextInterval return a null interval here. 
                    // So we do not test for a null condition because it 
                    // can't happen. If it happens,
                    // it means there is a bug in computeNextInterval or that
                    // the beginInstance list was messed with, which should not
                    // happen.
                    currentInterval.setBegin(curInt.begin);
                    currentInterval.setEnd(curInt.end);
                    computeLastDur(currentInterval);
                } 
                break;
            case STATE_PLAYING:
                if (newInstance.isBegin) {
                    if (restart == RESTART_ALWAYS) {
                        if (currentInterval.end
                                .greaterThan(newInstance.time)
                            &&
                            newInstance.time
                                .greaterThan(currentInterval.begin)) {
                            // Update the current interval end time with this
                            // new begin time.
                            currentInterval.setEnd(newInstance.time);
                            computeLastDur(currentInterval);
                            
                            // Next interval will be computed in the next call
                            // to sample();
                        }
                        // Ignore the new instance if it if falls after or
                        // before the current interval
                    }
                    // Ignore the new instance if we cannot restart this
                    // animation.
                } 
                break;
            default:
                throw new IllegalStateException();
            }
        } finally {
            timingUpdate = false;
        }
    
public voidbegin()
Calling this method causes a new TimeInstance to be inserted in the begin instance list. The behavior depends on the restart setting.

        beginAt(0);
    
public voidbeginAt(long offset)
Calling this method causes a new TimeInstance to be inserted in the begin instance list. The behavior depends on the restart setting.

param
offset the begin time inserted into the instance list is offset by 'offset' milliseconds from the current time.

        addInstance(true, offset);
    
TimecalculateActiveEnd(Time begin, Time end)
Computes the active end of this TimedElement for the given begin and end time constraints. This accounts for the other attributes which control or constrain the active duration.

param
begin the input begin time. Should not be unresolved or null.
param
end the proposed end time. May be null.
return
the computed, constrained end time.
see
SMIL 2: Computing the active duration

        Time result = end;
        Time pad = null;

        if (begin == null) {
            throw new NullPointerException();
        }
        
        if (!begin.isResolved()) {
            throw new IllegalArgumentException();
        }

        //
        // In the context of this method, end and begin are the time instances
        // passed to this method, and _not_ the begin and end attributes.
        //
        // If endTime is not null, and none of dur, repeatDur, and repeatCount
        // are specified, then the simple duration is indefinite from the simple
        // duration table above (see SMIL 2 spec), and the active duration is
        // defined by the end value, according to the following cases:
        //
        // If end is resolved to a value, then pad = end - B,
        // 
        // else, if end is indefinite, then pad = indefinite,
        // 
        // else, if end is unresolved, then pad is unresolved, and needs to be
        // recomputed when more information becomes available.
        //
        if (end != null 
            && dur == null 
            && repeatDur == null 
            && Float.isNaN(repeatCount)) {
            if (end.isResolved()) {
                pad = new Time(end.value - begin.value);
            } else if (end == Time.INDEFINITE) {
                pad = Time.INDEFINITE;
            } else {
                pad = Time.UNRESOLVED;
            }
        }


        // Else, if no end value is specified, or the end value is specified as
        // indefinite, then the active duration is determined from the
        // Intermediate Active Duration computation given below:
        // 
        // pad = Result from Intermediate Active Duration Computation  
        else if (end == null || end == Time.INDEFINITE) {
            pad = 
                calculateIntermediateActiveDuration(computeSimpleDuration(end));
        }
        
        // Otherwise, an end value not equal to indefinite is specified along
        // with at least one of dur, repeatDur, and repeatCount. Then the pad is
        // the minimum of the result from the Intermediate Active Duration
        // Computation given below and duration between end and the element
        // begin:
        //
        // pad = MIN( Result from Intermediate Active Duration Computation, 
        //            end - B)
        else {
            pad = 
                calculateIntermediateActiveDuration(computeSimpleDuration(end));
            Time pad2 = Time.UNRESOLVED;
            if (end.isResolved()) {
                pad2 = new Time(end.value - begin.value);
            }
            if (pad.greaterThan(pad2)) {
                pad = pad2;
            }
        }

        // Finally, the computed active duration ad is obtained by applying min
        // and max semantics to the preliminary active duration pad. In the
        // following expression, if there is no min value, substitute a value of
        // 0, and if there is no max value, substitute a value of "indefinite":
        // 
        // ad = MIN( max, MAX( min, pad )) 

        // Only do min/max constraint if min < max
        boolean doMinMax = true;
        if (min != null && max != null && !max.greaterThan(min)) {
            doMinMax = false;
        }

        Time ad = null;
        if (pad.isResolved()) {
            ad = new Time(pad.value);
        } else {
            ad = pad;
        }
            
        if (doMinMax) {
            // MAX( min, pad )
            if (min != null && min.greaterThan(ad)) {
                if (min.isResolved()) {
                    ad.value = min.value;
                } else {
                    ad = min;
                }
            }
            
            // MIN( max, MAX( min, pad ))
            if (max != null && !max.greaterThan(ad)) {
                if (max == Time.INDEFINITE) {
                    ad = Time.INDEFINITE;
                } else {
                    if (ad.isResolved()) {
                        ad.value = max.value;
                    } else {
                        ad = new Time(max.value);
                    }
                }
            }
        }

        // We have computed the active duration. Now, offset that
        // duration with the begin time to yield the computed end time.
        if (ad.isResolved()) {
            ad.value += begin.value;
        }

        // Finally, apply the restart behavior. If restart is always
        // we check if there is any end time that is before active
        // end time.
        if (restart == RESTART_ALWAYS) {
            Time restartBegin = getTimeAfterStrict(begin, beginInstances);
            if (restartBegin != null && ad.greaterThan(restartBegin)) {
                ad.value = restartBegin.value;
            }
        }

        return ad;
    
final TimecalculateIntermediateActiveDuration(Time p0)
Intermediate Active Duration Computation, as defined in the SMIL 2 specifications.

param
p0 the element's simple duration. Should not be null
return
the intermediate active duration.
see
Intermediate Active Duration Computation

        if (p0 == null) {
            throw new NullPointerException();
        }

        if (p0.isResolved() && p0.value == 0) {
            return new Time(0);
        } else if (repeatDur == null && Float.isNaN(repeatCount)) {
            return p0;
        } else {
            Time p1 = Time.INDEFINITE;
            Time p2 = repeatDur;
            Time iad = Time.UNRESOLVED;
            
            if (!Float.isNaN(repeatCount)) {
                if (p0.isResolved()) {
                    if (repeatCount == Float.MAX_VALUE) {
                        p1 = Time.INDEFINITE;
                    } else {
                        p1 = new Time((long) (p0.value * repeatCount));
                    }
                } else {
                    p1 = p0; // INDEFINITE or UNRESOLVED
                }
            }
            
            if (p2 == null) {
                p2 = Time.INDEFINITE;
            }
            
            iad = Time.INDEFINITE;
            if (!p2.greaterThan(iad)) {
                iad = p2;
            }
            if (!p1.greaterThan(iad)) {
                iad = p1;
            }
            return iad;
        }
    
booleancheckNewInterval(TimeInterval interval)
Checks if the input interval is non null. If it is, that interval becomes the new currentInterval and the element sets its state accordingly.

param
interval the interval to check.
return
true if the candidate interval has become the current interval.

        if (interval == null) {
            return false;
        } else {
            currentInterval = interval;
            simpleDur = computeSimpleDuration(currentInterval.end);

            // Transition to the current state
            // Note that we move to the 'playing' stage only in the
            // sample method, where event dispatching is done as well.
            if (previousInterval == null) {
                state = STATE_WAITING_INTERVAL_0;
            } else {
                state = STATE_WAITING_INTERVAL_N;
            }

            // Notify dependents of the new interval
            dispatchOnNewInterval();
            return true;
        }
    
public voidcheckPreInit()
Implementation helper. Throws an exception if the element is not in the STATE_PRE_INIT state.

throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.

        if (state != STATE_PRE_INIT) {
            throw new IllegalStateException();
        }
    
TimecomputeEndTime(Time tempBegin)
Computes the end time corresponding to the input begin time.

param
tempBegin the begin time for which to search an end time.
return
the computed end time or null if there is no matching end.

        if (endInstances.size() == 0) {
            return calculateActiveEnd(tempBegin, null);
        } else {
            Time tempEnd = getTimeAfter(tempBegin, endInstances);

            if (tempBegin.isSameTime(tempEnd)) {
                // IMPL NOTE : Do this to get a strictly superior end time
                tempEnd = new Time(tempEnd.value + 1);
                tempEnd = getTimeAfter(tempEnd, endInstances);
            }

            if (tempEnd == null) {
                if (endHasEventConditions() || endInstances.size() == 0) {
                    tempEnd = Time.UNRESOLVED;
                } else {
                    return null;
                }
            }

            return calculateActiveEnd(tempBegin, tempEnd);
        }
    
TimeIntervalcomputeFirstInterval()
The following computes the element's first interval, as defined in the SMIL specification.

return
the first time interval
see
STARTUP - Getting the first interval

        Time beginAfter = new Time(Long.MIN_VALUE);
        Time parentSimpleEnd = getContainerSimpleDuration();
        
        while (true) { // loop till return
            Time tempBegin = getTimeAfter(beginAfter, beginInstances);
            if (tempBegin == null) {
                return null; // No interval
            }

            if (tempBegin.greaterThan(parentSimpleEnd)) {
                return null; // No interval
            }

            Time tempEnd = null;
            if (endConditions.size() == 0) {
                tempEnd = calculateActiveEnd(tempBegin, null);
            } else {
                tempEnd = getTimeAfter(tempBegin, endInstances);
                if (tempBegin.isSameTime(tempEnd)) {
                    tempEnd = getTimeAfterStrict(tempEnd, endInstances);
                }

                if (tempEnd == null) {
                    if (endHasEventConditions() || endInstances.size() == 0) {
                        tempEnd = Time.UNRESOLVED;
                    } else {
                        return null; // No interval
                    }
                }

                tempEnd = calculateActiveEnd(tempBegin, tempEnd);
            }

            // We have an end - is it after the parent simple begin?
            if (!tempEnd.isResolved() || tempEnd.value > 0) {
                return computeLastDur(new TimeInterval(tempBegin, tempEnd));
            } else {
                beginAfter = tempEnd;
            }
        }
    
TimeIntervalcomputeLastDur(TimeInterval ti)
Computes the time of the last simple duration instance for the given time interval.

param
ti the TimeInterval instance for which the time of the last simple duration instance should be computed.
return
the input TimeInterval instance.

        Time simpleDur = computeSimpleDuration(ti.end);
        if (simpleDur != null) {
            Time iad = calculateIntermediateActiveDuration(simpleDur);
            if (iad.isResolved()) {
                ti.lastDur = new Time(ti.begin.value + iad.value);
                if (ti.lastDur.greaterThan(ti.end)) {
                    ti.lastDur = ti.end;
                }
            } else {
                ti.lastDur = ti.end;
            }
        } else {
            ti.lastDur = ti.end;
        }

        return ti;
    
TimeIntervalcomputeNextInterval()
The following computes the element's next interval, as defined in the SMIL specification.

return
the next interval given the current begin and end time instances.
see
End of an interval

        Time curEnd = previousInterval.end;
        Time beginAfter = curEnd;
        Time parentSimpleEnd = getContainerSimpleDuration();
        Time tempBegin = getTimeAfter(beginAfter, beginInstances);

        if (tempBegin == null) {
            return null;
        }
        
        if (tempBegin.greaterThan(parentSimpleEnd)) {
            return null;
        }

        Time tempEnd = null;
        if (endConditions.size() == 0) {
            tempEnd = calculateActiveEnd(tempBegin, null);
            if (endInstances.size() > 0) {
                // There is no end-attribute specified, but there are time 
                // instances. The SMIL Animation specification says that if
                // there are times instances from DOM Events or DOM 
                // beginElementAt calls, they short cut the active duration. 
                // 
                // See: http://www.w3.org/TR/2001/REC-smil-animation-20010904/
                // section 3.3.4.
                //
                Time tempEnd2 = getTimeAfter(tempBegin, endInstances);
                if (tempEnd2 != null && tempEnd2.isResolved()) {
                    tempEnd = tempEnd2;
                }
            }
        } else {
            // We have a begin value - get an end
            tempEnd = getTimeAfter(tempBegin, endInstances);

            if (curEnd.isSameTime(tempEnd)) {
                tempEnd = getTimeAfterStrict(tempEnd, endInstances);
            }

            if (tempEnd == null) {
                if (endHasEventConditions() || endInstances.size() == 0) {
                    tempEnd = Time.UNRESOLVED;
                } else {
                    return null;
                }
            }

            tempEnd = calculateActiveEnd(tempBegin, tempEnd);
        }

        return computeLastDur(new TimeInterval(tempBegin, tempEnd));
    
final TimecomputeSimpleDuration(Time end)
Computes the element's simple duration, as defined in the SMIL 2 specification except that the 'media' value is not supported. For the purpose of computing the simple duration, an unspecified dur or an UNRESOLVED dur yield the same computed simple duration.

param
end the interval end time. Can be null or have any Time value.
return
the computed simple duration.
see
Defining the simple duration

        Time implicitDur = getImplicitElementDuration();

        //    dur          | implicit    | repeatDur and  | simpleDuration
        //                 | duration    | repeatCount    |
        //    -------------+-------------+----------------+--------------------
        // 1. unspecified  | ignored     | unspecified,   | indefinite
        //                 |             | end specified  |
        // 2. Clock-value  | ignored     | ignored        | dur or Clock-value
        // 3. indefinite   | ignored     | ignored        | indefinite
        // 4. unspecified  | resolved    | ignored        | implicit duration
        // 5. unspecified  | unresolved  | ignored        | unresolved
        //    -------------+-------------+----------------+--------------------
        
        // First line case
        if (dur == null && repeatDur == null
            && Float.isNaN(repeatCount) && end != Time.UNRESOLVED) {
            return Time.INDEFINITE;
        }

        // Second & Third line case
        if (dur != null && dur != Time.UNRESOLVED) {
            return dur;
        }

        // Fourth line case. If we got to this point, we know 
        // dur is UNRESOLVED or null
        if (implicitDur != Time.UNRESOLVED) {
            return implicitDur;
        } else {
            // Fifth line
            return Time.UNRESOLVED;
        }
    
voiddispatchBeginEvent(Time currentTime)
Dispatches beginEvent. As per the SMIL 2 specification, this dispatches a beginEvent for the resolved begin time, not the observed begin time. It also dispatches any repeat event that might be needed.

param
currentTime the current sampling time.
see
SMIL2's Begin value semantics

        if (animationElement != null) {
            ModelEvent beginEvent = null;
            if (seeking) {
                beginEvent = animationElement.ownerDocument.initEngineEvent
                    (SEEK_BEGIN_EVENT_TYPE, animationElement);
            } else {
                beginEvent = animationElement.ownerDocument.initEngineEvent
                    (BEGIN_EVENT_TYPE, animationElement);
            }

            eventTime.value = currentInterval.begin.value;
            beginEvent.eventTime = toRootContainerSimpleTimeClamp(eventTime);
            animationElement.dispatchEvent(beginEvent);
        }

        dispatchRepeatEvent(currentTime);
    
voiddispatchEndEvent(Time currentTime)
Dispatches endEvent. As per the SMIL 2 specification, this dispatches an endEvent for the resolved end time, not the observed end time.

param
currentTime the current sampling time.
see
SMIL2's Begin value semantics

        if ((!seeking || state == STATE_PLAYING)
            && animationElement != null) {
            ModelEvent endEvent = animationElement.ownerDocument.initEngineEvent
                (END_EVENT_TYPE, animationElement);
            if (seeking) {
                // We are seeking to a new currentTime and the interval
                // ends during the seek interval and the element was
                // active at the time of seek. The time for the end
                // event should be the time _before_ the seek
                eventTime.value = getRootContainer().lastSampleTime.value;
                endEvent.eventTime = eventTime;
            } else {
                eventTime.value = currentInterval.end.value;
                endEvent.eventTime = toRootContainerSimpleTimeClamp(eventTime);
            }
            
            animationElement.dispatchEvent(endEvent);
        }
        dispatchRepeatEvent(currentTime);
    
voiddispatchLastDurEndEvent()
Dispatches lastDurEndEvent.

see
SMIL2's Begin value semantics

        if (!seeking && simpleDur.isResolved() 
            && simpleDur.value > 0) {
            int prevIter = curIter;

            Time maxTime = currentTime;
            if (currentTime.greaterThan(currentInterval.end)) {
                maxTime = currentInterval.end;
            }

            curIter = (int) ((maxTime.value - currentInterval.begin.value) 
                             / 
                             simpleDur.value);

            if (curIter < 0) {
                curIter = 0;
            }

            if (maxTime.isSameTime(currentInterval.end)
                && 
                ((maxTime.value - currentInterval.begin.value) 
                 % 
                 simpleDur.value 
                 == 0)) {
                curIter--;
            }

            if (animationElement != null) {
                for (int i = prevIter; i < curIter; i++) {
                    ModelEvent repeatEvent 
                        = animationElement.ownerDocument.initEngineEvent
                        (REPEAT_EVENT_TYPE, animationElement);
                    
                    // The event time is the time at which the repeat happened,
                    // not the time at which it was detected (that would be
                    // currentTime).  So we have to compute the repeat time here
                    Time repeatTime = eventTime;
                    repeatTime.value = currentInterval.begin.value
                        + (i + 1) * simpleDur.value;
                    repeatTime = toRootContainerSimpleTimeClamp(repeatTime);
                    repeatEvent.eventTime = repeatTime;
                    repeatEvent.repeatCount = i + 1;
                    animationElement.dispatchEvent(repeatEvent);
                }
            }

            if (prevIter != curIter) {
                onStartingRepeat(prevIter, curIter);
            }
        }
    
voiddispatchSeekEndEvent()
Dispatches seekeEndEvent.

see
TimegetTimeAfter(Time after, java.util.Vector instances)

param
after we are looking for a time greater than or equal to after.
param
instances the Vector containing the TimeInstances to look up.
return
the first value in the given instance list that starts after the input time.

        int n = instances.size();
        
        // NOTE: The following _assumes_ that the instances vector
        //       is sorted in increasing time order.
        for (int i = 0; i < n; i++) {
            TimeInstance timeInstance = (TimeInstance) instances.elementAt(i);
            if (timeInstance.time.greaterThan(after)) {
                return timeInstance.time;
            }
        }
        return null;
    
TimegetTimeAfterStrict(Time after, java.util.Vector instances)

param
after we are looking for a time greater than or equal to after.
param
instances the Vector containing the TimeInstances to look up.
return
the first value in the given instance list that starts strictly after the input time.

        int n = instances.size();
        
        // NOTE: The following _assumes_ that the instances vector
        //       is sorted in increasing time order.
        for (int i = 0; i < n; i++) {
            TimeInstance timeInstance = (TimeInstance) instances.elementAt(i);
            if (!after.greaterThan(timeInstance.time)) {
                return timeInstance.time;
            }
        }
        return null;
    
protected TimeContainerSupportgetTimeContainer()

return
this TimedElement's container.

        return timeContainer;
    
booleanhasBeginCondition(EventBaseCondition eventCondition)

param
eventCondition the EventBaseCondition to check against.
return
true if this TimedElement has a begin condition corresponding to the input EventBaseCondition

        int n = beginConditions.size();
        for (int i = 0; i < n; i++) {
            TimeCondition condition = 
                (TimeCondition) beginConditions.elementAt(i);
            if (condition instanceof EventBaseCondition) {
                EventBaseCondition beginCondition = 
                    (EventBaseCondition) condition;
                if (beginCondition.eventType.equals(eventCondition.eventType)
                    &&
                    beginCondition.eventBase == eventCondition.eventBase) {
                    return true;
                }
                                                    
            }
        }

        return false;
    
protected voidinitialize()
This method is called by the element's time container when it resets, i.e., when the container starts it's simple time again. This has the effect of: 1. clearing all the instance times which have the clearOnReset flag set to true. 2. compute the first interval.

        // Clear instances with clearOnReset == true
        reset();

        // Compute the first interval
        if (!checkNewInterval(computeFirstInterval())) {
            state = STATE_NO_INTERVAL;
        }
    
private voidinsertTimeInstance(TimeInstance newInstance)
This method inserts the input time instance in the instance list

param
newInstance the instance to reposition in its instance list
see
#addTimeInstance
see
#onTimeInstanceUpdate

        Vector instances = beginInstances;
        if (!newInstance.isBegin) {
            instances = endInstances;
        }

        int n = instances.size();
        int i = 0;
        Time newTime = newInstance.time;
        for (i = 0; i < n; i++) {
            TimeInstance timeInstance 
                = (TimeInstance) instances.elementAt(i);
            if (!newTime.greaterThan(timeInstance.time)) {
                instances.insertElementAt(newInstance, i);
                break;
            }
        }

        if (i == n) {
            instances.addElement(newInstance);
        }
    
booleanisDescendant(TimeContainerSupport parent)

param
parent the time container which might be on the timed element's parent hierarchy.
return
true if this timed element is equal to 'parent' or is a descendant of 'parent'

        if (parent == this) {
            return true;
        } else if (timeContainer != this) {
            return timeContainer.isDescendant(parent);
        } else {
            return false;
        }
    
booleanisSeekingBack()
Helper method.

return
true if the root time container is flagged as seeking back.

        return getRootContainer().seekingBack;
    
protected voidonStartingRepeat(int prevIter, int curIter)
To be overridden by extensions (TimeContainerSupport) to do specific processing when a new iteration is started.

param
prevIter the last iteration this element was playing.
param
curIter the new iteration this element is playing.

    
voidonTimeInstanceUpdate(TimeInstance timeInstance)
Called by TimeInstances when they are updated. In response, the TimedElement may recompute its current interval.

param
timeInstance the TimeInstance that was just updated.
throws
NullPointerException if timeInstance is null.
see
SMIL 2: Building the instance times lists

        if (timingUpdate) {
            // We are in a timing dependency cycle, break now.
            return;
        }

        timingUpdate = true;

        try {
            // First, reposition the instance in the instance list
            if (timeInstance.isBegin) {
                beginInstances.removeElement(timeInstance);
            } else {
                endInstances.removeElement(timeInstance);
            }
            insertTimeInstance(timeInstance);
            
            if (state == STATE_PRE_INIT) {
                return;
            }
            
            if (timeInstance.isBegin) {
                // We are dealing with a begin instance change.
                reEvaluateBeginTime();
            } else {
                // We are dealing with an end time instance.
                reEvaluateEndTime();
            }
        } finally {
            timingUpdate = false;
        }
    
private voidpruneCurrentInterval()
Helper method invoked when the current interval becomes invalid after re-evaluation of the begin and end instances.

see
SMIL 2: Principles for building and pruning intervals

        TimeInterval prunedInterval = currentInterval;
        currentInterval = null;
        if (previousInterval != null) {
            state = STATE_FILL;
        } else {
            state = STATE_NO_INTERVAL;
        }

        prunedInterval.prune();
    
private voidreEvaluateBeginTime()
Should be called whenever there is a change in the begin time instance list.

        switch (state) {
        case STATE_WAITING_INTERVAL_0:
            {
                TimeInterval curInt = computeFirstInterval();
                if (curInt != null) {
                    currentInterval.setBegin(curInt.begin);
                    currentInterval.setEnd(curInt.end);
                    computeLastDur(currentInterval);
                } else {
                    // There no first interval any more, after
                    // the begin instance has been changed.
                    // We move back to the no interval state
                    pruneCurrentInterval();
                }
            }
            break;
        case STATE_WAITING_INTERVAL_N:
            {
                TimeInterval curInt = computeNextInterval();
                if (curInt != null) {
                    currentInterval.setBegin(curInt.begin);
                    currentInterval.setEnd(curInt.end);
                    computeLastDur(currentInterval);
                } else {
                    pruneCurrentInterval();
                }
            }
            
        default:
            return;
        }
    
voidreEvaluateEndTime()
Recomputes the end time. This only does something if the element is playing or waiting to play, because it may update the currentInterval's end time.

        switch (state) {
        case STATE_WAITING_INTERVAL_0:
        case STATE_WAITING_INTERVAL_N:
        case STATE_PLAYING:
            // We are dealing with a new end time and the animation is
            // playing. Re-evaluate the end time and update the 
            // current interval.
            //
            // Note that we cannot simply do a computeNextInterval as this
            // might yield a begin time different from the one on the 
            // playing interval.
            //
            Time newEnd = computeEndTime(currentInterval.begin);

            // It is unclear what should happen here if newEnd is null.
            // This could happen if a list of end times was empty and
            // a SyncBaseCondition, for example, inserts a new end instance
            // that is _before_ the currentInterval's begin time. In that
            // situation, should we just ignore newEnd or should we 
            // prune the currentInterval (if WAITING_0 or WAITING_N) and
            // stop it (if PLAYING)?
            //
            // Pending further investigation of this corner case, we simply
            // ignore a null newEnd
            if (newEnd != null) {
                currentInterval.setEnd(newEnd);
                computeLastDur(currentInterval);
            }
            break;
        default:
            break;
        }
    
voidremoveSyncBaseTimesUnder(TimeContainerSupport syncTimeContainer)
Removes all IntervalTimeInstances in the begin and end instance list if the syncBase is a descendant of syncTimeContainer

param
syncTimeContainer the container under which IntervalTimeInstance should be removed.

        int n = beginInstances.size();
        for (int i = n - 1; i >= 0; i--) {
            TimeInstance timeInstance 
                = (TimeInstance) beginInstances.elementAt(i);
            if (timeInstance instanceof IntervalTimeInstance) {
                IntervalTimeInstance iti = (IntervalTimeInstance) timeInstance;
                if (iti.syncBase.isDescendant(syncTimeContainer)) {
                    beginInstances.removeElementAt(i);
                    iti.dispose();
                }
            } 
        }
        n = endInstances.size();
        for (int i = n - 1; i >= 0; i--) {
            TimeInstance timeInstance = 
                    (TimeInstance) endInstances.elementAt(i);
            if (timeInstance instanceof IntervalTimeInstance) {
                IntervalTimeInstance iti = (IntervalTimeInstance) timeInstance;
                if (iti.syncBase.isDescendant(syncTimeContainer)) {
                    endInstances.removeElementAt(i);
                    iti.dispose();
                }
            } 
        }
    
voidremoveTimeInstance(TimeInstance timeInstance)
Called to remove a TimeInstance when a TimeInterval is pruned. This causes a re-evaluation of the current interval's begin or end time.

param
timeInstance the instance to remove from its instance list.

        if (timeInstance.isBegin) {
            beginInstances.removeElement(timeInstance);
        } else {
            endInstances.removeElement(timeInstance);
        }
        
        if (timingUpdate) {
            return;
        }
        
        timingUpdate = true;
        try {
            if (state != STATE_PRE_INIT) {
                if (timeInstance.isBegin) {
                    reEvaluateBeginTime();
                } else {
                    reEvaluateEndTime();
                }
            }
        } finally {
            timingUpdate = false;
        }
    
voidreset()
Resetting the element clears all of its 'clearOnReset' instances from the begin and end list. This includes event based time instances, time instances resulting from begin() or end() calls and resolved IntervalTimeInstance.

        currentInterval = null;
        simpleDur = Time.UNRESOLVED;
        previousInterval = null;
        curIter = 0;
        state = STATE_PRE_INIT;
        lastSampleTime = -1;

        int n = beginInstances.size();
        for (int i = n - 1; i >= 0; i--) {
            TimeInstance timeInstance 
                = (TimeInstance) beginInstances.elementAt(i);
            if (timeInstance.clearOnReset) {
                beginInstances.removeElementAt(i);
            } else if (timeInstance instanceof IntervalTimeInstance) {
                ((IntervalTimeInstance) timeInstance).syncTime();
            } 
        }
        n = endInstances.size();
        for (int i = n - 1; i >= 0; i--) {
            TimeInstance timeInstance 
                = (TimeInstance) endInstances.elementAt(i);
            if (timeInstance.clearOnReset) {
                endInstances.removeElementAt(i);
            } else if (timeInstance instanceof IntervalTimeInstance) {
                ((IntervalTimeInstance) timeInstance).syncTime();
            } 
        }
    
voidsample(Time currentTime)
This method is typically called by this element's time container when it samples. Note that if this element is not in the waiting or playing state, this does nothing. This method assumes that successive calls are made with increasing time values.

param
currentTime the time at which this element should be sampled.

        // Handle state transitions if there is a current interval
        boolean endOfInterval = false;
        boolean seekBack = false;

        switch (state) {
        default:
            return;
        case STATE_FILL:
            if (seeking && isSeekingBack()) {
                seekBack = true;
            }
            break;
        case STATE_WAITING_INTERVAL_0:
            if (currentTime.greaterThan(currentInterval.begin)) {
                // This element has begun between the previous sampling
                // and now. Dispatch the beginEvent.
                dispatchBeginEvent(currentTime);
                
                if (currentTime.greaterThan(currentInterval.end)) {
                    // This element has also ended between the 
                    // previous sampling and now. Dispatch the endEvent
                    dispatchLastDurEndEvent();
                    dispatchEndEvent(currentTime);
                    endOfInterval = true;
                }

                state = STATE_PLAYING;
                playFill = false;
            }
            break;
        case STATE_WAITING_INTERVAL_N:
            if (currentTime.greaterThan(currentInterval.begin)) {
                // This element has begun between the previous sampling
                // and now. Dispatch the beginEvent.
                dispatchBeginEvent(currentTime);
                
                if (currentTime.greaterThan(currentInterval.end)) {
                    // This element has also ended between the 
                    // previous sampling and now. Dispatch the endEvent
                    dispatchLastDurEndEvent();
                    dispatchEndEvent(currentTime);
                    endOfInterval = true;
                }

                state = STATE_PLAYING;
                playFill = false;
            } else {
                if (seeking && isSeekingBack()) {
                    seekBack = true;
                }
            }
            break;
        case STATE_PLAYING:
            if (currentTime.greaterThan(currentInterval.lastDur)) {
                if (!playFill) {
                    dispatchLastDurEndEvent();
                    playFill = true;
                }
            }

            if (currentTime.greaterThan(currentInterval.end)) {
                // This element has also ended between the 
                // previous sampling and now. Dispatch the endEvent
                dispatchEndEvent(currentTime);
                endOfInterval = true;
            } else {
                if (!seeking) {
                    dispatchRepeatEvent(currentTime);
                } else if (currentInterval.begin.greaterThan(currentTime)) {
                    // We are seeking backwards and the current interval
                    // began during the seek interval. We need to 
                    // dispatch and end event at the time before the 
                    // seek.
                    dispatchLastDurEndEvent();
                    dispatchEndEvent(currentTime);
                    endOfInterval = true;
                    seekBack = true;
                }
            }
            break;
        }

        // If we just finished an interval,
        // we need to compute the new interval now
        if (endOfInterval) {
            previousInterval = currentInterval;
            currentInterval = null;

            // If we cannot restart, we just go to the fill state
            if (restart == RESTART_NEVER) {
                state = STATE_FILL;
                playFill = false;
            } else {
                TimeInterval nextInterval = null;
                if (!seekBack) {
                    nextInterval = computeNextInterval();
                } else {
                    nextInterval = seekToInterval(currentTime);
                }
                
                if (!checkNewInterval(nextInterval)) {
                    // There is no next interval, we move to the 
                    // fill state.
                    state = STATE_FILL;
                    playFill = false;
                }
            }
             
            // This recursive call is needed in case we sample so far ahead 
            // that we are skipping several intervals.
            // For example if the intervals are:
            // [0, 1000[
            // [2000, 3000[
            // [5000, 6500[
            // [8000, 1000[
            // and we sample at 0 and then 7000
            sample(currentTime);
            return;
        } else if (seekBack) {
            // If we are in the FILL state and we cannot restart, then we stay
            // in FILL state if seeking to after the previous interval's
            // end. Otherwise, we move to the STATE_NO_INTERVAL state.
            if (state == STATE_FILL && restart == RESTART_NEVER) {
                if (previousInterval.end.greaterThan(currentTime)) {
                    dispatchSeekEndEvent();
                    state = STATE_NO_INTERVAL;
                    playFill = false;
                    return;
                } 
            } else {
                TimeInterval seekInterval = seekToInterval(currentTime);
                
                // If the seek interval is the same as the current interval, we
                // do not need to do further sampling because we were in, or
                // have already moved to, the right interval.
                if (seekInterval == null 
                    || currentInterval == null 
                    || !(seekInterval.begin.isSameTime(currentInterval.begin)
                         &&
                         seekInterval.end.isSameTime(currentInterval.end))) {
                    
                    // We need to check if there is an interval active at the
                    // time we are seeking to.
                    if (!checkNewInterval(seekInterval)) {
                        // There is no currentInterval. 
                        // Switch to the right state 
                        if (previousInterval != null) {
                            state = STATE_FILL;
                            playFill = false;
                        } else {
                            dispatchSeekEndEvent();
			    state = STATE_NO_INTERVAL;
                            playFill = false;
                            return;
                        }
                    } else {
                        // There is an interval at the time we are 
                        // seeking to. End the current interval (which
                        // we know is different because of the previous if
                        // statement) and sample again to have the new current
                        // interval kick-in.
                        dispatchSeekEndEvent();
                        sample(currentTime);
                        return;
                    }
                }
            }
        }
        
        // 
        // Different behaviors depending on the state. At this point, we
        // should only be in one of the following states:
        // - STATE_FILL
        // - STATE_PLAYING
        // - STATE_WAITING_INTERVAL_0
        // - STATE_WAITING_INTERVAL_N
        // 
        long localTime = 0;
        switch (state) {
        default:
            // This should _never_ happen, given this method's code.
            // This is extremely hard to test (would require changing
            // state during the execution of this method) and does 
            // not need to be covered by the test suite. 
            throw new IllegalStateException();
        case STATE_WAITING_INTERVAL_N:
        case STATE_FILL:
            if (fillBehavior == FILL_BEHAVIOR_FREEZE) {
                // If we are in STATE_WAITING_INTERVAL_N or STATE_FILL,
                // it means that we had a previous interval. Therefore,
                // previousInterval is guaranteed to be not null.
                localTime = previousInterval.lastDur.value 
                            - previousInterval.begin.value;
            } else {
                return;
            }
            break;
        case STATE_WAITING_INTERVAL_0:
            return;
        case STATE_PLAYING:
            // If we are still in the PLAYING state but we are past the
            // end of the last simple duration end, make sure we sample at
            // that last simple duration time.
            if (!playFill) {
                localTime = currentTime.value - currentInterval.begin.value;
            } else {
                localTime = currentInterval.lastDur.value 
                    - currentInterval.begin.value;
            }
            break;
        }

        // If we get to this point, it means we were able to
        // compute a localTime we need to sample at. We are
        // either playing the animation or we are in a frozen
        // state. Compute the simple time from the localTime.
        if (simpleDur.isResolved()) {
            localTime = localTime % simpleDur.value;
            if (state != STATE_PLAYING || playFill) {
                // If we are not playing, it means we are frozen
                // (either STATE_WAITING_N or STATE_FILL).
                //
                // Make sure we sample on the end of the simple time 
                // if we froze at the end of the interval.
                // 
                // Note that we do not need to test if the value
                // was 0 before %. Because we are _not_ playing, it
                // means we are _not_ on the first value of the 
                // interval, therefore, we never run into the condition
                // where localTime would be 0 before % and therefore,
                // we do not need to test for that condition
                //
                if (localTime == 0) {
                    // Freeze on the last value in the simple duration
                    // interval only when we freeze.
                    localTime = simpleDur.value;
                }
            }
        }

        // At this point, we have computed our simple time.
        // Ready to connect with the animation engine.
        sampleAt(localTime);

        // Keep the last simpleTime this element was sampled at
        lastSampleTime = localTime;
    
voidsampleAt(long simpleTime)
Samples this element at the given simple time. Should be overridden for specific behaviors.

param
simpleTime this timed element simple time.

    
voidseekTo(Time seekToTime)
Implementation helper method for seekTo behavior.

param
seekToTime the time to seek to.

        if (seekToTime.isResolved()) {
            // Now, move up the container graph.
            timeContainer.seekTo(seekToTime);
        } else {
            // 3. Else (i.e. there are no defined intervals for the element),
            // the target element begin time must be resolved. This may require
            // seeking and/or resolving ancestor elements as well. This is done
            // by recursing from the target element up to the closest ancestor
            // element that has a resolved begin time (again noting that for an
            // element to have a resolved begin time, all of its ancestors must
            // have resolved begin times).
            if (timeContainer.state != STATE_PLAYING) {
                TimeInterval firstContainerInterval 
                    = timeContainer.computeFirstInterval();
                if (firstContainerInterval == null) {
                    timeContainer.seekTo(Time.UNRESOLVED);
                } else {
                    Time parentSeekTo 
                        = new Time(firstContainerInterval.begin.value);
                    parentSeekTo 
                        = timeContainer
                        .toRootContainerSimpleTimeClamp(parentSeekTo);
                    timeContainer.seekTo(parentSeekTo);
                }
            }

            // Then, the recursion is "unwound", and for each ancestor in turn
            // (beneath the resolved ancestor) as well as the target element,
            // the following steps are performed:
            //
            //     1. If the element begin time is resolved, seek the document
            //     time (forward or back, as needed) to the begin time of the
            //     first interval for the target element.
            //
            //     2. Else (if the begin time is not resolved), just resolve the
            //     element begin time at the current time on its parent time
            //     container (given the current document position). Disregard
            //     the sync-base or event base of the element, and do not
            //     "back-propagate" any timing logic to resolve the element, but
            //     rather treat it as though it were defined with
            //     begin="indefinite" and just resolve begin time to the current
            //     parent time. This should create an interval and propagate to
            //     time dependents.
            //
            TimeInterval firstInterval = computeFirstInterval();
            if (firstInterval == null) {
                begin();
                firstInterval = computeFirstInterval();
            }
            

            Time goToTime = new Time(firstInterval.begin.value);
            if (goToTime.value < 0) {
                goToTime.value = 0;
            }

            timeContainer.seekTo(toRootContainerSimpleTimeClamp(goToTime));
        }
    
TimeIntervalseekToInterval(Time seekToTime)
Helper method to find the active interval, or next interval, for the requested time.

param
seekToTime the time we are seeking the active or next interval for.
return
the TimeInterval corresponding to seekToTime

        previousInterval = null;
        TimeInterval interval = computeFirstInterval();
        if (interval == null) {
            return null;
        }

        while (interval != null && seekToTime.greaterThan(interval.end)) {
            previousInterval = interval;
            interval = computeNextInterval();
        }

        return interval;
    
public voidsetDur(Time dur)
Sets the duration for this timed element

param
dur the new element duration.
throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.

        checkPreInit();
        this.dur = dur;
    
public voidsetFillBehavior(int fillBehavior)
Sets the fill state behavior.

param
fillBehavior the new fill strategy, one of FILL_BEHAVIOR_FREEZE or FILL_BEHAVIOR_REMOVE
throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.

        checkPreInit();
        switch (fillBehavior) {
        case FILL_BEHAVIOR_FREEZE:
        case FILL_BEHAVIOR_REMOVE:
            this.fillBehavior = fillBehavior;
            break;
        default:
            throw new IllegalArgumentException();
        }
    
public voidsetMax(Time max)
Sets the maximun active duration for any timed element interval.

param
max the new maximum active duration. Should not be null.
throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.

        checkPreInit();
        if (max == null) {
            throw new IllegalArgumentException();
        }
        this.max = max;
    
public voidsetMin(Time min)
Sets the minimum active duration for any timed element interval.

param
min the new minimum active duration. Should not be null.
throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.

        checkPreInit();
        if (min == null) {
            throw new IllegalArgumentException();
        }
        this.min = min;
    
public voidsetRepeatCount(float repeatCount)
Sets the repeat count.

param
repeatCount the new repeat count. Must be greater than zero.
throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.

        checkPreInit();

        if (repeatCount <= 0) {
            throw new IllegalArgumentException();
        }

        this.repeatCount = repeatCount;
    
public voidsetRepeatDur(Time repeatDur)
Sets the repeat duration.

param
repeatDur the new repeat duration.
throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.


                                       
         
        checkPreInit();
        this.repeatDur = repeatDur;
    
public voidsetRestart(int restart)
Sets the restart strategy.

param
restart the new restart strategy, one of RESTART_ALWAYS, RESTART_NEVER or RESTART_WHEN_NOT_ACTIVE.
throws
IllegalStateException if the element is not in the STATE_PRE_INIT state.

        checkPreInit();
        switch (restart) {
        case RESTART_ALWAYS:
        case RESTART_NEVER:
        case RESTART_WHEN_NOT_ACTIVE:
            this.restart = restart;
            break;
        default:
            throw new IllegalArgumentException();
        }
    
protected voidsetTimeContainer(TimeContainerSupport timeContainer)
Sets this timed element's time container

param
timeContainer time container

        if (this.timeContainer != null) {
            this.timeContainer.timedElementChildren.removeElement(this);
        }

        this.timeContainer = timeContainer;
        if (timeContainer != null) {
            timeContainer.timedElementChildren.addElement(this);
            if (timeContainer.state != STATE_PRE_INIT) {
                // This is a live addition of an animation to a container.
                // We need to initialize this timed element support.
                initialize();
            }
        }
    
TimetoContainerSimpleTime(Time simpleTime)
Converts the input root container simple time (i.e., a time in the root container's simple time interval) to a time in this element's time container simple duration. Note that this method mutates the input argument. If the associated time container does not have a current interval, this method returns Time.UNRESOLVED.

param
simpleTime the time in the root container's simple duration. If simpleTime is Time.UNRESOLVED, the returned time is UNRESOLVED. If simpleTime is Time.INDEFINITE, the returned time is INDEFINITE.
return
a simple time in the parent container's simple duration The return value is in the [0, container simple duration] interval.

        if (timeContainer.currentInterval == null) {
            return Time.UNRESOLVED;
        }

        if (!simpleTime.isResolved()) {
            return simpleTime;
        }

        // Convertion to the container's container simple time
        simpleTime = timeContainer.toContainerSimpleTime(simpleTime);
        
        // Account for the container's begin value
        simpleTime.value -= timeContainer.currentInterval.begin.value;

        // Account for repeat behavior
        if (timeContainer.simpleDur.isResolved()) {
            simpleTime.value -= 
                timeContainer.curIter * timeContainer.simpleDur.value;
        }

        return simpleTime;
    
TimetoRootContainerSimpleTime(Time simpleTime)
Converts the input simple time (i.e., a time in the parent container's simple duration) to a root container simple time (i.e., a time in the root time container's simple time interval). Note that this method mutates the input time value. If the associated time container does not have a current interval, this method returns Time.UNRESOLVED.

param
simpleTime the time in the parent container's simple duration. Should not be null. If simpleTime is Time.UNRESOLVED, the returned time is UNRESOLVED. If simpleTime is Time.INDEFINITE, the returned time is INDEFINITE.
return
a time in the root time container's simple duration (i.e., in the root container's simple time interval).

        if (timeContainer.currentInterval == null) {
            return Time.UNRESOLVED;
        }

        if (!simpleTime.isResolved()) {
            return simpleTime;
        }

        // Convert to the container's container simple time

        // Account for repeat behavior. This yields a time
        // in the [0, ActiveTime[ time system
        if (timeContainer.simpleDur.isResolved()) {
            simpleTime.value += 
                timeContainer.curIter * timeContainer.simpleDur.value;
        }

        // By adding the timeContainer's begin value, we are
        // translating the simpleTime to a time in the container's
        // container simple duration [0, simpleDur[ interval.
        simpleTime.value += timeContainer.currentInterval.begin.value;

        // Now, move the conversion one level up.
        return timeContainer.toRootContainerSimpleTime(simpleTime);
    
TimetoRootContainerSimpleTimeClamp(Time simpleTime)
Same definition and behavior as toRootContainerSimpleTime, except that this method clamps values to the container's simple duration. This means that a value smaller than zero is converted to 0 and a value greater than the simple duration is reduced to the simple duration.

param
simpleTime the time in the parent container's simple duration. Should not be null. If simpleTime is Time.UNRESOLVED, the returned time is UNRESOLVED. If simpleTime is Time.INDEFINITE, the returned time is INDEFINITE.
return
a time in the root time container's simple duration (i.e., in the root container's simple time interval).

        if (timeContainer.currentInterval == null) {
            return Time.UNRESOLVED;
        }

        if (!simpleTime.isResolved()) {
            return simpleTime;
        }

        // Clamp to zero.
        if (simpleTime.value < 0) {
            simpleTime.value = 0;
        }

        // Convert to the container's _container_ simple time

        // Account for repeat behavior. This yields a time
        // in the [0, ActiveTime[ time system
        if (timeContainer.simpleDur.isResolved()) {
            // Clamp to the simple duration.
            if (simpleTime.value > timeContainer.simpleDur.value) {
                simpleTime.value = timeContainer.simpleDur.value;
            }
            simpleTime.value += 
                timeContainer.curIter * timeContainer.simpleDur.value;
        } else {
            // Still clamp to the container's active duration.
            if (timeContainer.currentInterval.end.isResolved()) {
                long maxDur = timeContainer.currentInterval.end.value 
                    - timeContainer.currentInterval.begin.value;
                if (simpleTime.value > maxDur) {
                    simpleTime.value = maxDur;
                }
            }
        }

        // By adding the timeContainer's begin value, we are
        // translating the simpleTime to a time in the container's
        // container simple duration [0, simpleDur[ interval.
        simpleTime.value += timeContainer.currentInterval.begin.value;

        // Now, move the conversion one level up.
        return timeContainer.toRootContainerSimpleTimeClamp(simpleTime);
    
public java.lang.StringtoString()
Debug helper.

return
a description of this object.

        String str = "[Animation: " + animationElement + "] [" 
                        + getStateString() + "]";
        switch (state) {
        case STATE_WAITING_INTERVAL_N:
        case STATE_WAITING_INTERVAL_0:
        case STATE_PLAYING:
            str += "[" 
                + currentInterval.begin 
                + ", " 
                + currentInterval.end 
                /*
                + ", "
                + currentInterval.lastDur
                */
                + "]";
            break;
        default:
            break;
        }
        return str;
    
longtoWallClockTime(long localTime)
Converts the input 'local' time to an absolute wallclock time.

param
localTime the time to convert to wallclock time
return
the time converted to an absolute wallclock time.

        return timeContainer.toWallClockTime(localTime);