Source Code Cross Referenced for TimedElementSupport.java in  » 6.0-JDK-Modules » j2me » com » sun » perseus » model » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » j2me » com.sun.perseus.model 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *
0003:         *
0004:         * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006:         * 
0007:         * This program is free software; you can redistribute it and/or
0008:         * modify it under the terms of the GNU General Public License version
0009:         * 2 only, as published by the Free Software Foundation.
0010:         * 
0011:         * This program is distributed in the hope that it will be useful, but
0012:         * WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014:         * General Public License version 2 for more details (a copy is
0015:         * included at /legal/license.txt).
0016:         * 
0017:         * You should have received a copy of the GNU General Public License
0018:         * version 2 along with this work; if not, write to the Free Software
0019:         * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020:         * 02110-1301 USA
0021:         * 
0022:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023:         * Clara, CA 95054 or visit www.sun.com if you need additional
0024:         * information or have any questions.
0025:         */
0026:        package com.sun.perseus.model;
0027:
0028:        import java.util.Vector;
0029:
0030:        /**
0031:         * The <code>TimedElementSupport</code> class is the main abstraction of the
0032:         * SMIL timing model implementation.
0033:         *
0034:         * <p>It is responsible for computing the animation's state (inactive, active,
0035:         * post-acitve, frozen) and, when the application is active, what the current
0036:         * simple time is. That simple time is then used by the animation engine to
0037:         * compute animated values. </p>
0038:         * 
0039:         * <p>SMIL has a notion of time containers. A <code>TimedElementSupport</code>
0040:         * is the child of a time container. The relationship is that the element's
0041:         * current time is the container's simple time. In otherwords, sampling a
0042:         * container for a given time (in its own container), yields a simple time for
0043:         * the container. That simple time is the timed element's current time, used to
0044:         * compute the timed element's own simple time. Refer to the SMIL 2
0045:         * specification for an explanation of what the various time semantics are. </p>
0046:         *
0047:         * @version $Id: TimedElementSupport.java,v 1.7 2006/07/13 00:55:58 st125089 Exp $
0048:         */
0049:        public class TimedElementSupport {
0050:            /**
0051:             * Last Simple Duration End event type.
0052:             */
0053:            public static final String LAST_DUR_END_EVENT_TYPE = "lastDurEndEvent";
0054:
0055:            /**
0056:             * Seek End event type.
0057:             */
0058:            public static final String SEEK_END_EVENT_TYPE = "seekEndEvent";
0059:
0060:            /**
0061:             * Seek End event type.
0062:             */
0063:            public static final String SEEK_BEGIN_EVENT_TYPE = "seekBeginEvent";
0064:
0065:            /**
0066:             * End event type.
0067:             */
0068:            public static final String END_EVENT_TYPE = "endEvent";
0069:
0070:            /**
0071:             * Begin event type.
0072:             */
0073:            public static final String BEGIN_EVENT_TYPE = "beginEvent";
0074:
0075:            /**
0076:             * Repeat event type
0077:             */
0078:            public static final String REPEAT_EVENT_TYPE = "repeat";
0079:
0080:            /**
0081:             * Used when this <code>TimedElement</code> can be restarted under
0082:             * any circumstance.
0083:             */
0084:            public static final int RESTART_ALWAYS = 1;
0085:
0086:            /**
0087:             * Used when this <code>TimedElement</code> can only be restarted when
0088:             * it is not active (i.e., it does not have a current interval.
0089:             */
0090:            public static final int RESTART_WHEN_NOT_ACTIVE = 2;
0091:
0092:            /**
0093:             * Used when this <code>TimedElement</code> cannot be restarted under
0094:             * any circumstance
0095:             */
0096:            public static final int RESTART_NEVER = 3;
0097:
0098:            /**
0099:             * Used when this <code>TimedElement</code> should not modify the
0100:             * animated value when it is post active.
0101:             */
0102:            public static final int FILL_BEHAVIOR_REMOVE = 1;
0103:
0104:            /**
0105:             * Used when this <code>TimedElement</code> should maintain the
0106:             * last value of its simple interval after it becomes inactive.
0107:             */
0108:            public static final int FILL_BEHAVIOR_FREEZE = 2;
0109:
0110:            /**
0111:             * State before the element has been initialized
0112:             */
0113:            protected static final int STATE_PRE_INIT = 1;
0114:
0115:            /**
0116:             * State where there has never been a current interval
0117:             * (since the last init).
0118:             */
0119:            protected static final int STATE_NO_INTERVAL = 2;
0120:
0121:            /**
0122:             * State while the element is waiting to begin
0123:             * the first interval.
0124:             */
0125:            protected static final int STATE_WAITING_INTERVAL_0 = 3;
0126:
0127:            /**
0128:             * State while the element is waiting to begin
0129:             * an interval other than the first one
0130:             */
0131:            protected static final int STATE_WAITING_INTERVAL_N = 4;
0132:
0133:            /**
0134:             * State while the element is playing the current
0135:             * interval.
0136:             */
0137:            protected static final int STATE_PLAYING = 5;
0138:
0139:            /**
0140:             * State when the element performs any fill on the previous
0141:             * interval.
0142:             */
0143:            protected static final int STATE_FILL = 6;
0144:
0145:            /**
0146:             * This time value is used to avoid creating Time instances
0147:             * over and over again in animation loops, when dispatching
0148:             * events.
0149:             */
0150:            protected final Time eventTime = new Time(0);
0151:
0152:            /**
0153:             * The element's state. One of the STATE_XXX constants.
0154:             */
0155:            int state = STATE_PRE_INIT;
0156:
0157:            /**
0158:             * Controls whether the element is in this rare state
0159:             * where it is playing but its last simple duration 
0160:             * is finished. In that state, the element is 'playing',
0161:             * but acting as if frozen.
0162:             *
0163:             * There are situations where the min attribute may cause the 
0164:             * active duration to go beyond the time of the last simple
0165:             * duration instance. In that case, the element is still
0166:             * in the playing state, but we mark it as 'playFill', which
0167:             * means that it should behave almost as if in the fill state.
0168:             */
0169:            boolean playFill = false;
0170:
0171:            /**
0172:             * The current iteration (only used while playing
0173:             */
0174:            int curIter = 0;
0175:
0176:            /**
0177:             * This element's simple duration. This corresponds to the 'dur'
0178:             * attribute. Note that this is only one of the parameters that
0179:             * defines the actual simple duration.
0180:             *
0181:             * @see #simpleDur
0182:             * @see #computeSimpleDuration
0183:             */
0184:            Time dur = Time.INDEFINITE;
0185:
0186:            /**
0187:             * The number of times this element repeats the simple duration.
0188:             * NaN means unspecified.
0189:             */
0190:            float repeatCount = Float.NaN;
0191:
0192:            /**
0193:             * repeatDur set a limit on active duration length.
0194:             */
0195:            Time repeatDur = null;
0196:
0197:            /**
0198:             * This element's implicit duration
0199:             */
0200:            Time implicitDuration = Time.UNRESOLVED;
0201:
0202:            /**
0203:             * min sets a lower limit on the active duration.
0204:             */
0205:            Time min = new Time(0);
0206:
0207:            /**
0208:             * max sets an upper limit on the active duration
0209:             */
0210:            Time max = Time.INDEFINITE;
0211:
0212:            /**
0213:             * Defines the restart behavior for this element. This should be
0214:             * one of RESTART_ALWAYS, RESTART_WHEN_NOT_ACTIVE, RESTART_NEVER.
0215:             */
0216:            int restart = RESTART_ALWAYS;
0217:
0218:            /**
0219:             * Defines the behavior after this element is no longer active.
0220:             * If FILL_BEHAVIOR_FILL, it means the element will keep sampling at the 
0221:             * last simple duration time. If FILL_BEHAVIOR_REMOVE (the default),
0222:             * the effect of the animation is removed when the element is 
0223:             * inactive (i.e., when it no longer has a current interval.
0224:             */
0225:            int fillBehavior = FILL_BEHAVIOR_REMOVE;
0226:
0227:            /**
0228:             * A reference to this element's time container. This is initialized
0229:             * anytime the parent is set. This element's current time is the 
0230:             * container's simple time.
0231:             * 
0232:             * @see #setParent
0233:             */
0234:            protected TimeContainerSupport timeContainer;
0235:
0236:            /**
0237:             * Keeps a reference to the current <code>TimeInterval</code>
0238:             */
0239:            TimeInterval currentInterval;
0240:
0241:            /**
0242:             * Last local time (i.e., within the simple duration) this element
0243:             * was sampled at.
0244:             */
0245:            long lastSampleTime = -1;
0246:
0247:            /**
0248:             * The simple duration for the current interval
0249:             * @see #computeSimpleDuration
0250:             */
0251:            Time simpleDur = Time.UNRESOLVED;
0252:
0253:            /**
0254:             * Keeps a reference to the interval preceding the current one.
0255:             */
0256:            TimeInterval previousInterval;
0257:
0258:            /**
0259:             * Keeps are reference to the last interval that was actually
0260:             * run.
0261:             */
0262:
0263:            /**
0264:             * List of begin <code>TimeInstance</code>s.
0265:             */
0266:            Vector beginInstances = new Vector(1);
0267:
0268:            /**
0269:             * List of end <code>TimeInstance</code>s.
0270:             */
0271:            Vector endInstances = new Vector(1);
0272:
0273:            /**
0274:             * Begin <code>TimeCondition</code>s. Should _never_ be null.
0275:             */
0276:            Vector beginConditions = new Vector(1);
0277:
0278:            /**
0279:             * End <code>TimeCondition</code>s. Should _never_ be null.
0280:             */
0281:            Vector endConditions = new Vector(1);
0282:
0283:            /**
0284:             * List of <code>SyncBaseCondition</code>s depedent on this
0285:             * element's begin condition.
0286:             */
0287:            Vector beginDependents;
0288:
0289:            /**
0290:             * List of <code>SyncBaseCondition</code>s dependent on this 
0291:             * element's end condition.
0292:             */
0293:            Vector endDependents;
0294:
0295:            /**
0296:             * The associated animation element.
0297:             */
0298:            ModelNode animationElement;
0299:
0300:            /**
0301:             * Time dependency cycles detector. This flag is set to true
0302:             * when a timing update starts. There should only be one at
0303:             * a time, and it should always be set back to true after
0304:             * and timing update is complete.
0305:             */
0306:            boolean timingUpdate = false;
0307:
0308:            /**
0309:             * The seeking flag is used when seeking to a particular time
0310:             * to prevent generation of beginEvent, endEvent and repeat
0311:             * event.
0312:             */
0313:            boolean seeking = false;
0314:
0315:            /**
0316:             * Sets the repeat duration. 
0317:             *
0318:             * @param repeatDur the new repeat duration.
0319:             * @throws IllegalStateException if the element is not in the
0320:             *         STATE_PRE_INIT state.
0321:             */
0322:            public void setRepeatDur(final Time repeatDur) {
0323:                checkPreInit();
0324:                this .repeatDur = repeatDur;
0325:            }
0326:
0327:            /**
0328:             * Sets the repeat count.
0329:             *
0330:             * @param repeatCount the new repeat count. Must be greater than
0331:             *        zero.
0332:             * @throws IllegalStateException if the element is not in the
0333:             *         STATE_PRE_INIT state.
0334:             */
0335:            public void setRepeatCount(final float repeatCount) {
0336:                checkPreInit();
0337:
0338:                if (repeatCount <= 0) {
0339:                    throw new IllegalArgumentException();
0340:                }
0341:
0342:                this .repeatCount = repeatCount;
0343:            }
0344:
0345:            /**
0346:             * Sets the restart strategy.
0347:             *
0348:             * @param restart the new restart strategy, one of
0349:             *         RESTART_ALWAYS, RESTART_NEVER or RESTART_WHEN_NOT_ACTIVE.
0350:             * @throws IllegalStateException if the element is not in the
0351:             *         STATE_PRE_INIT state.
0352:             */
0353:            public void setRestart(final int restart) {
0354:                checkPreInit();
0355:                switch (restart) {
0356:                case RESTART_ALWAYS:
0357:                case RESTART_NEVER:
0358:                case RESTART_WHEN_NOT_ACTIVE:
0359:                    this .restart = restart;
0360:                    break;
0361:                default:
0362:                    throw new IllegalArgumentException();
0363:                }
0364:            }
0365:
0366:            /**
0367:             * Sets the maximun active duration for any timed element
0368:             * interval.
0369:             *
0370:             * @param max the new maximum active duration. Should not be
0371:             *        null.
0372:             * @throws IllegalStateException if the element is not in the
0373:             *         STATE_PRE_INIT state.
0374:             */
0375:            public void setMax(final Time max) {
0376:                checkPreInit();
0377:                if (max == null) {
0378:                    throw new IllegalArgumentException();
0379:                }
0380:                this .max = max;
0381:            }
0382:
0383:            /**
0384:             * Sets the minimum active duration for any timed element interval.
0385:             *
0386:             * @param min the new minimum active duration. Should not be null.
0387:             * @throws IllegalStateException if the element is not in the
0388:             *         STATE_PRE_INIT state.
0389:             */
0390:            public void setMin(final Time min) {
0391:                checkPreInit();
0392:                if (min == null) {
0393:                    throw new IllegalArgumentException();
0394:                }
0395:                this .min = min;
0396:            }
0397:
0398:            /**
0399:             * Sets the duration for this timed element
0400:             *
0401:             * @param dur the new element duration.
0402:             * @throws IllegalStateException if the element is not in the
0403:             *         STATE_PRE_INIT state.
0404:             */
0405:            public void setDur(final Time dur) {
0406:                checkPreInit();
0407:                this .dur = dur;
0408:            }
0409:
0410:            /**
0411:             * Sets the fill state behavior.
0412:             *
0413:             * @param fillBehavior the new fill strategy, one of FILL_BEHAVIOR_FREEZE or
0414:             *        FILL_BEHAVIOR_REMOVE
0415:             * @throws IllegalStateException if the element is not in the 
0416:             *         STATE_PRE_INIT state.
0417:             */
0418:            public void setFillBehavior(final int fillBehavior) {
0419:                checkPreInit();
0420:                switch (fillBehavior) {
0421:                case FILL_BEHAVIOR_FREEZE:
0422:                case FILL_BEHAVIOR_REMOVE:
0423:                    this .fillBehavior = fillBehavior;
0424:                    break;
0425:                default:
0426:                    throw new IllegalArgumentException();
0427:                }
0428:            }
0429:
0430:            /**
0431:             * Implementation helper. Throws an exception if the element is
0432:             * not in the STATE_PRE_INIT state.
0433:             *
0434:             * @throws IllegalStateException if the element is not in the
0435:             *         STATE_PRE_INIT state.
0436:             */
0437:            public void checkPreInit() {
0438:                if (state != STATE_PRE_INIT) {
0439:                    throw new IllegalStateException();
0440:                }
0441:            }
0442:
0443:            /**
0444:             * Calling this method causes a new <code>TimeInstance</code> to
0445:             * be inserted in the begin instance list. The behavior depends
0446:             * on the restart setting.
0447:             */
0448:            public void begin() {
0449:                beginAt(0);
0450:            }
0451:
0452:            /**
0453:             * Calling this method causes a new <code>TimeInstance</code> to
0454:             * be inserted in the end instance list. This typically causes
0455:             * the current interval (if any), to be ended.
0456:             */
0457:            public void end() {
0458:                endAt(0);
0459:            }
0460:
0461:            /**
0462:             * Calling this method causes a new <code>TimeInstance</code> to
0463:             * be inserted in the begin instance list. The behavior depends
0464:             * on the restart setting.
0465:             *
0466:             * @param offset the begin time inserted into the instance list
0467:             *        is offset by 'offset' milliseconds from the current
0468:             *        time.
0469:             */
0470:            public void beginAt(final long offset) {
0471:                addInstance(true, offset);
0472:            }
0473:
0474:            /**
0475:             * Calling this method causes a new <code>TimeInstance</code> to
0476:             * be inserted in the end instance list. This typically causes
0477:             * the current interval (if any), to be ended.
0478:             *
0479:             * @param offset the end time inserted into the instance list
0480:             *        is offset by 'offset' milliseconds from the current
0481:             *        time.
0482:             */
0483:            public void endAt(final long offset) {
0484:                addInstance(false, offset);
0485:            }
0486:
0487:            /**
0488:             * Adds a new <code>TimedInstance</code> with the specified offset
0489:             * to the begin or end instance list (depending on <code>isBegin</code>
0490:             *
0491:             * @param isBegin if true, the instance is a begin instance
0492:             * @param offset the instance offset from 0.
0493:             */
0494:            void addInstance(final boolean isBegin, final long offset) {
0495:                Time currentTime = getCurrentTime();
0496:                if (!currentTime.isResolved()) {
0497:                    // If the current time is not resolved, it means the time
0498:                    // container is not active. Therefore, there is no way
0499:                    // we can add a new time instance that would be meaningful. 
0500:                    // So we do nothing.
0501:                    return;
0502:                }
0503:
0504:                Time newTime = new Time(currentTime.value + offset);
0505:
0506:                TimeInstance newInstance = new TimeInstance(this , newTime,
0507:                        true, isBegin);
0508:            }
0509:
0510:            /**
0511:             * Sets this timed element's time container
0512:             *
0513:             * @param timeContainer time container
0514:             */
0515:            protected void setTimeContainer(
0516:                    final TimeContainerSupport timeContainer) {
0517:                if (this .timeContainer != null) {
0518:                    this .timeContainer.timedElementChildren.removeElement(this );
0519:                }
0520:
0521:                this .timeContainer = timeContainer;
0522:                if (timeContainer != null) {
0523:                    timeContainer.timedElementChildren.addElement(this );
0524:                    if (timeContainer.state != STATE_PRE_INIT) {
0525:                        // This is a live addition of an animation to a container.
0526:                        // We need to initialize this timed element support.
0527:                        initialize();
0528:                    }
0529:                }
0530:            }
0531:
0532:            /**
0533:             * @return this TimedElement's container.
0534:             */
0535:            protected TimeContainerSupport getTimeContainer() {
0536:                return timeContainer;
0537:            }
0538:
0539:            /**
0540:             * This method is called by the element's time container when it
0541:             * resets, i.e., when the container starts it's simple time again.
0542:             *
0543:             * This has the effect of:
0544:             * 1. clearing all the instance times which have the clearOnReset
0545:             *    flag set to true.
0546:             * 2. compute the first interval.
0547:             */
0548:            protected void initialize() {
0549:                // Clear instances with clearOnReset == true
0550:                reset();
0551:
0552:                // Compute the first interval
0553:                if (!checkNewInterval(computeFirstInterval())) {
0554:                    state = STATE_NO_INTERVAL;
0555:                }
0556:            }
0557:
0558:            /**
0559:             * Calls all the registered <code>TimeDependent</code>s so that they
0560:             * are notified of a new TimeInterval creation.
0561:             */
0562:            void dispatchOnNewInterval() {
0563:                int n = beginDependents == null ? 0 : beginDependents.size();
0564:                for (int i = 0; i < n; i++) {
0565:                    ((TimeDependent) beginDependents.elementAt(i))
0566:                            .onNewInterval(this );
0567:                }
0568:
0569:                n = endDependents == null ? 0 : endDependents.size();
0570:                for (int i = 0; i < n; i++) {
0571:                    ((TimeDependent) endDependents.elementAt(i))
0572:                            .onNewInterval(this );
0573:                }
0574:            }
0575:
0576:            /**
0577:             * @param after we are looking for a time greater than or equal
0578:             *        to after.
0579:             * @param instances the Vector containing the TimeInstances to
0580:             *        look up.
0581:             * @return the first value in the given instance list that
0582:             *         starts after the input time.
0583:             */
0584:            Time getTimeAfter(final Time after, final Vector instances) {
0585:                int n = instances.size();
0586:
0587:                // NOTE: The following _assumes_ that the instances vector
0588:                //       is sorted in increasing time order.
0589:                for (int i = 0; i < n; i++) {
0590:                    TimeInstance timeInstance = (TimeInstance) instances
0591:                            .elementAt(i);
0592:                    if (timeInstance.time.greaterThan(after)) {
0593:                        return timeInstance.time;
0594:                    }
0595:                }
0596:                return null;
0597:            }
0598:
0599:            /**
0600:             * @param after we are looking for a time greater than or equal
0601:             *        to after.
0602:             * @param instances the Vector containing the TimeInstances to
0603:             *        look up.
0604:             * @return the first value in the given instance list that
0605:             *         starts strictly after the input time.
0606:             */
0607:            Time getTimeAfterStrict(final Time after, final Vector instances) {
0608:                int n = instances.size();
0609:
0610:                // NOTE: The following _assumes_ that the instances vector
0611:                //       is sorted in increasing time order.
0612:                for (int i = 0; i < n; i++) {
0613:                    TimeInstance timeInstance = (TimeInstance) instances
0614:                            .elementAt(i);
0615:                    if (!after.greaterThan(timeInstance.time)) {
0616:                        return timeInstance.time;
0617:                    }
0618:                }
0619:                return null;
0620:            }
0621:
0622:            /**
0623:             * Computes the active end of this <code>TimedElement</code> for
0624:             * the given begin and end time constraints. This accounts for 
0625:             * the other attributes which control or constrain the 
0626:             * active duration.
0627:             *
0628:             * @param begin the input begin time. Should not be unresolved or null.
0629:             * @param end the proposed end time. May be null.
0630:             * @return the computed, constrained end time.
0631:             *
0632:             * @see <a
0633:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-ComputingActiveDur">
0634:             * SMIL 2: Computing the active duration</a>
0635:             */
0636:            Time calculateActiveEnd(final Time begin, final Time end) {
0637:                Time result = end;
0638:                Time pad = null;
0639:
0640:                if (begin == null) {
0641:                    throw new NullPointerException();
0642:                }
0643:
0644:                if (!begin.isResolved()) {
0645:                    throw new IllegalArgumentException();
0646:                }
0647:
0648:                //
0649:                // In the context of this method, end and begin are the time instances
0650:                // passed to this method, and _not_ the begin and end attributes.
0651:                //
0652:                // If endTime is not null, and none of dur, repeatDur, and repeatCount
0653:                // are specified, then the simple duration is indefinite from the simple
0654:                // duration table above (see SMIL 2 spec), and the active duration is
0655:                // defined by the end value, according to the following cases:
0656:                //
0657:                // If end is resolved to a value, then pad = end - B,
0658:                // 
0659:                // else, if end is indefinite, then pad = indefinite,
0660:                // 
0661:                // else, if end is unresolved, then pad is unresolved, and needs to be
0662:                // recomputed when more information becomes available.
0663:                //
0664:                if (end != null && dur == null && repeatDur == null
0665:                        && Float.isNaN(repeatCount)) {
0666:                    if (end.isResolved()) {
0667:                        pad = new Time(end.value - begin.value);
0668:                    } else if (end == Time.INDEFINITE) {
0669:                        pad = Time.INDEFINITE;
0670:                    } else {
0671:                        pad = Time.UNRESOLVED;
0672:                    }
0673:                }
0674:
0675:                // Else, if no end value is specified, or the end value is specified as
0676:                // indefinite, then the active duration is determined from the
0677:                // Intermediate Active Duration computation given below:
0678:                // 
0679:                // pad = Result from Intermediate Active Duration Computation  
0680:                else if (end == null || end == Time.INDEFINITE) {
0681:                    pad = calculateIntermediateActiveDuration(computeSimpleDuration(end));
0682:                }
0683:
0684:                // Otherwise, an end value not equal to indefinite is specified along
0685:                // with at least one of dur, repeatDur, and repeatCount. Then the pad is
0686:                // the minimum of the result from the Intermediate Active Duration
0687:                // Computation given below and duration between end and the element
0688:                // begin:
0689:                //
0690:                // pad = MIN( Result from Intermediate Active Duration Computation, 
0691:                //            end - B)
0692:                else {
0693:                    pad = calculateIntermediateActiveDuration(computeSimpleDuration(end));
0694:                    Time pad2 = Time.UNRESOLVED;
0695:                    if (end.isResolved()) {
0696:                        pad2 = new Time(end.value - begin.value);
0697:                    }
0698:                    if (pad.greaterThan(pad2)) {
0699:                        pad = pad2;
0700:                    }
0701:                }
0702:
0703:                // Finally, the computed active duration ad is obtained by applying min
0704:                // and max semantics to the preliminary active duration pad. In the
0705:                // following expression, if there is no min value, substitute a value of
0706:                // 0, and if there is no max value, substitute a value of "indefinite":
0707:                // 
0708:                // ad = MIN( max, MAX( min, pad )) 
0709:
0710:                // Only do min/max constraint if min < max
0711:                boolean doMinMax = true;
0712:                if (min != null && max != null && !max.greaterThan(min)) {
0713:                    doMinMax = false;
0714:                }
0715:
0716:                Time ad = null;
0717:                if (pad.isResolved()) {
0718:                    ad = new Time(pad.value);
0719:                } else {
0720:                    ad = pad;
0721:                }
0722:
0723:                if (doMinMax) {
0724:                    // MAX( min, pad )
0725:                    if (min != null && min.greaterThan(ad)) {
0726:                        if (min.isResolved()) {
0727:                            ad.value = min.value;
0728:                        } else {
0729:                            ad = min;
0730:                        }
0731:                    }
0732:
0733:                    // MIN( max, MAX( min, pad ))
0734:                    if (max != null && !max.greaterThan(ad)) {
0735:                        if (max == Time.INDEFINITE) {
0736:                            ad = Time.INDEFINITE;
0737:                        } else {
0738:                            if (ad.isResolved()) {
0739:                                ad.value = max.value;
0740:                            } else {
0741:                                ad = new Time(max.value);
0742:                            }
0743:                        }
0744:                    }
0745:                }
0746:
0747:                // We have computed the active duration. Now, offset that
0748:                // duration with the begin time to yield the computed end time.
0749:                if (ad.isResolved()) {
0750:                    ad.value += begin.value;
0751:                }
0752:
0753:                // Finally, apply the restart behavior. If restart is always
0754:                // we check if there is any end time that is before active
0755:                // end time.
0756:                if (restart == RESTART_ALWAYS) {
0757:                    Time restartBegin = getTimeAfterStrict(begin,
0758:                            beginInstances);
0759:                    if (restartBegin != null && ad.greaterThan(restartBegin)) {
0760:                        ad.value = restartBegin.value;
0761:                    }
0762:                }
0763:
0764:                return ad;
0765:            }
0766:
0767:            /**
0768:             * Intermediate Active Duration Computation, as defined in the 
0769:             * SMIL 2 specifications. 
0770:             *
0771:             * @param p0 the element's simple duration. Should not be null
0772:             * @return the intermediate active duration.
0773:             * @see <a href="http://www.w3.org/TR/smil20/smil-timing.html#q81">
0774:             *                  Intermediate Active Duration Computation</a>
0775:             */
0776:            final Time calculateIntermediateActiveDuration(final Time p0) {
0777:                if (p0 == null) {
0778:                    throw new NullPointerException();
0779:                }
0780:
0781:                if (p0.isResolved() && p0.value == 0) {
0782:                    return new Time(0);
0783:                } else if (repeatDur == null && Float.isNaN(repeatCount)) {
0784:                    return p0;
0785:                } else {
0786:                    Time p1 = Time.INDEFINITE;
0787:                    Time p2 = repeatDur;
0788:                    Time iad = Time.UNRESOLVED;
0789:
0790:                    if (!Float.isNaN(repeatCount)) {
0791:                        if (p0.isResolved()) {
0792:                            if (repeatCount == Float.MAX_VALUE) {
0793:                                p1 = Time.INDEFINITE;
0794:                            } else {
0795:                                p1 = new Time((long) (p0.value * repeatCount));
0796:                            }
0797:                        } else {
0798:                            p1 = p0; // INDEFINITE or UNRESOLVED
0799:                        }
0800:                    }
0801:
0802:                    if (p2 == null) {
0803:                        p2 = Time.INDEFINITE;
0804:                    }
0805:
0806:                    iad = Time.INDEFINITE;
0807:                    if (!p2.greaterThan(iad)) {
0808:                        iad = p2;
0809:                    }
0810:                    if (!p1.greaterThan(iad)) {
0811:                        iad = p1;
0812:                    }
0813:                    return iad;
0814:                }
0815:            }
0816:
0817:            /**
0818:             * Computes the element's simple duration, as defined in the 
0819:             * SMIL 2 specification except that the 'media' value is
0820:             * not supported.
0821:             *
0822:             * For the purpose of computing the simple duration, an unspecified
0823:             * dur or an UNRESOLVED dur yield the same computed simple duration.
0824:             *
0825:             * @param end the interval end time. Can be null or have any Time
0826:             *        value.
0827:             * @return the computed simple duration.
0828:             * @see <a
0829:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-DefiningSimpleDur">
0830:             * Defining the simple duration</a>
0831:             */
0832:            final Time computeSimpleDuration(final Time end) {
0833:                Time implicitDur = getImplicitElementDuration();
0834:
0835:                //    dur          | implicit    | repeatDur and  | simpleDuration
0836:                //                 | duration    | repeatCount    |
0837:                //    -------------+-------------+----------------+--------------------
0838:                // 1. unspecified  | ignored     | unspecified,   | indefinite
0839:                //                 |             | end specified  |
0840:                // 2. Clock-value  | ignored     | ignored        | dur or Clock-value
0841:                // 3. indefinite   | ignored     | ignored        | indefinite
0842:                // 4. unspecified  | resolved    | ignored        | implicit duration
0843:                // 5. unspecified  | unresolved  | ignored        | unresolved
0844:                //    -------------+-------------+----------------+--------------------
0845:
0846:                // First line case
0847:                if (dur == null && repeatDur == null
0848:                        && Float.isNaN(repeatCount) && end != Time.UNRESOLVED) {
0849:                    return Time.INDEFINITE;
0850:                }
0851:
0852:                // Second & Third line case
0853:                if (dur != null && dur != Time.UNRESOLVED) {
0854:                    return dur;
0855:                }
0856:
0857:                // Fourth line case. If we got to this point, we know 
0858:                // dur is UNRESOLVED or null
0859:                if (implicitDur != Time.UNRESOLVED) {
0860:                    return implicitDur;
0861:                } else {
0862:                    // Fifth line
0863:                    return Time.UNRESOLVED;
0864:                }
0865:            }
0866:
0867:            /**
0868:             * @return this element's implicit duration. Can be used for media such
0869:             *         as audio and video where there is a natural, implicit duration.
0870:             */
0871:            protected Time getImplicitElementDuration() {
0872:                return implicitDuration;
0873:            }
0874:
0875:            /**
0876:             * @return true if there is at least one event end condition.
0877:             */
0878:            boolean endHasEventConditions() {
0879:                for (int i = 0; i < endConditions.size(); i++) {
0880:                    if (endConditions.elementAt(i) instanceof  EventBaseCondition) {
0881:                        return true;
0882:                    }
0883:                }
0884:                return false;
0885:            }
0886:
0887:            /**
0888:             * Computes the time of the last simple duration instance
0889:             * for the given time interval.
0890:             *
0891:             * @param ti the TimeInterval instance for which the time of the last
0892:             *        simple duration instance should be computed.
0893:             *
0894:             * @return the input TimeInterval instance.
0895:             */
0896:            TimeInterval computeLastDur(final TimeInterval ti) {
0897:                Time simpleDur = computeSimpleDuration(ti.end);
0898:                if (simpleDur != null) {
0899:                    Time iad = calculateIntermediateActiveDuration(simpleDur);
0900:                    if (iad.isResolved()) {
0901:                        ti.lastDur = new Time(ti.begin.value + iad.value);
0902:                        if (ti.lastDur.greaterThan(ti.end)) {
0903:                            ti.lastDur = ti.end;
0904:                        }
0905:                    } else {
0906:                        ti.lastDur = ti.end;
0907:                    }
0908:                } else {
0909:                    ti.lastDur = ti.end;
0910:                }
0911:
0912:                return ti;
0913:            }
0914:
0915:            /**
0916:             * The following computes the element's first interval, as defined
0917:             * in the SMIL specification.
0918:             *
0919:             * @return the first time interval
0920:             * @see <a
0921:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-BeginEnd-LC-Start">
0922:             * STARTUP - Getting the first interval</a>
0923:             */
0924:            TimeInterval computeFirstInterval() {
0925:                Time beginAfter = new Time(Long.MIN_VALUE);
0926:                Time parentSimpleEnd = getContainerSimpleDuration();
0927:
0928:                while (true) { // loop till return
0929:                    Time tempBegin = getTimeAfter(beginAfter, beginInstances);
0930:                    if (tempBegin == null) {
0931:                        return null; // No interval
0932:                    }
0933:
0934:                    if (tempBegin.greaterThan(parentSimpleEnd)) {
0935:                        return null; // No interval
0936:                    }
0937:
0938:                    Time tempEnd = null;
0939:                    if (endConditions.size() == 0) {
0940:                        tempEnd = calculateActiveEnd(tempBegin, null);
0941:                    } else {
0942:                        tempEnd = getTimeAfter(tempBegin, endInstances);
0943:                        if (tempBegin.isSameTime(tempEnd)) {
0944:                            tempEnd = getTimeAfterStrict(tempEnd, endInstances);
0945:                        }
0946:
0947:                        if (tempEnd == null) {
0948:                            if (endHasEventConditions()
0949:                                    || endInstances.size() == 0) {
0950:                                tempEnd = Time.UNRESOLVED;
0951:                            } else {
0952:                                return null; // No interval
0953:                            }
0954:                        }
0955:
0956:                        tempEnd = calculateActiveEnd(tempBegin, tempEnd);
0957:                    }
0958:
0959:                    // We have an end - is it after the parent simple begin?
0960:                    if (!tempEnd.isResolved() || tempEnd.value > 0) {
0961:                        return computeLastDur(new TimeInterval(tempBegin,
0962:                                tempEnd));
0963:                    } else {
0964:                        beginAfter = tempEnd;
0965:                    }
0966:                }
0967:            }
0968:
0969:            /**
0970:             * The following computes the element's next interval, as defined
0971:             * in the SMIL specification.
0972:             *
0973:             * @return the next interval given the current begin and end time instances.
0974:             * @see <a
0975:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-BeginEnd-LC-End">
0976:             * End of an interval</a>
0977:             */
0978:            TimeInterval computeNextInterval() {
0979:                Time curEnd = previousInterval.end;
0980:                Time beginAfter = curEnd;
0981:                Time parentSimpleEnd = getContainerSimpleDuration();
0982:                Time tempBegin = getTimeAfter(beginAfter, beginInstances);
0983:
0984:                if (tempBegin == null) {
0985:                    return null;
0986:                }
0987:
0988:                if (tempBegin.greaterThan(parentSimpleEnd)) {
0989:                    return null;
0990:                }
0991:
0992:                Time tempEnd = null;
0993:                if (endConditions.size() == 0) {
0994:                    tempEnd = calculateActiveEnd(tempBegin, null);
0995:                    if (endInstances.size() > 0) {
0996:                        // There is no end-attribute specified, but there are time 
0997:                        // instances. The SMIL Animation specification says that if
0998:                        // there are times instances from DOM Events or DOM 
0999:                        // beginElementAt calls, they short cut the active duration. 
1000:                        // 
1001:                        // See: http://www.w3.org/TR/2001/REC-smil-animation-20010904/
1002:                        // section 3.3.4.
1003:                        //
1004:                        Time tempEnd2 = getTimeAfter(tempBegin, endInstances);
1005:                        if (tempEnd2 != null && tempEnd2.isResolved()) {
1006:                            tempEnd = tempEnd2;
1007:                        }
1008:                    }
1009:                } else {
1010:                    // We have a begin value - get an end
1011:                    tempEnd = getTimeAfter(tempBegin, endInstances);
1012:
1013:                    if (curEnd.isSameTime(tempEnd)) {
1014:                        tempEnd = getTimeAfterStrict(tempEnd, endInstances);
1015:                    }
1016:
1017:                    if (tempEnd == null) {
1018:                        if (endHasEventConditions() || endInstances.size() == 0) {
1019:                            tempEnd = Time.UNRESOLVED;
1020:                        } else {
1021:                            return null;
1022:                        }
1023:                    }
1024:
1025:                    tempEnd = calculateActiveEnd(tempBegin, tempEnd);
1026:                }
1027:
1028:                return computeLastDur(new TimeInterval(tempBegin, tempEnd));
1029:            }
1030:
1031:            /**
1032:             * Computes the end time corresponding to the input begin time.
1033:             *
1034:             * @param tempBegin the begin time for which to search an end time.
1035:             * @return the computed end time or null if there is no matching end.
1036:             */
1037:            Time computeEndTime(final Time tempBegin) {
1038:                if (endInstances.size() == 0) {
1039:                    return calculateActiveEnd(tempBegin, null);
1040:                } else {
1041:                    Time tempEnd = getTimeAfter(tempBegin, endInstances);
1042:
1043:                    if (tempBegin.isSameTime(tempEnd)) {
1044:                        // IMPL NOTE : Do this to get a strictly superior end time
1045:                        tempEnd = new Time(tempEnd.value + 1);
1046:                        tempEnd = getTimeAfter(tempEnd, endInstances);
1047:                    }
1048:
1049:                    if (tempEnd == null) {
1050:                        if (endHasEventConditions() || endInstances.size() == 0) {
1051:                            tempEnd = Time.UNRESOLVED;
1052:                        } else {
1053:                            return null;
1054:                        }
1055:                    }
1056:
1057:                    return calculateActiveEnd(tempBegin, tempEnd);
1058:                }
1059:            }
1060:
1061:            /**
1062:             * Resetting the element clears all of its 'clearOnReset' instances from the
1063:             * begin and end list. This includes event based time instances, time
1064:             * instances resulting from begin() or end() calls and resolved
1065:             * IntervalTimeInstance.
1066:             */
1067:            void reset() {
1068:                currentInterval = null;
1069:                simpleDur = Time.UNRESOLVED;
1070:                previousInterval = null;
1071:                curIter = 0;
1072:                state = STATE_PRE_INIT;
1073:                lastSampleTime = -1;
1074:
1075:                int n = beginInstances.size();
1076:                for (int i = n - 1; i >= 0; i--) {
1077:                    TimeInstance timeInstance = (TimeInstance) beginInstances
1078:                            .elementAt(i);
1079:                    if (timeInstance.clearOnReset) {
1080:                        beginInstances.removeElementAt(i);
1081:                    } else if (timeInstance instanceof  IntervalTimeInstance) {
1082:                        ((IntervalTimeInstance) timeInstance).syncTime();
1083:                    }
1084:                }
1085:                n = endInstances.size();
1086:                for (int i = n - 1; i >= 0; i--) {
1087:                    TimeInstance timeInstance = (TimeInstance) endInstances
1088:                            .elementAt(i);
1089:                    if (timeInstance.clearOnReset) {
1090:                        endInstances.removeElementAt(i);
1091:                    } else if (timeInstance instanceof  IntervalTimeInstance) {
1092:                        ((IntervalTimeInstance) timeInstance).syncTime();
1093:                    }
1094:                }
1095:            }
1096:
1097:            /**
1098:             * Removes all <code>IntervalTimeInstance</code>s in the begin and end
1099:             * instance list if the syncBase is a descendant of syncTimeContainer
1100:             *
1101:             * @param syncTimeContainer the container under which
1102:             *        <code>IntervalTimeInstance</code> should be removed.
1103:             */
1104:            void removeSyncBaseTimesUnder(
1105:                    final TimeContainerSupport syncTimeContainer) {
1106:                int n = beginInstances.size();
1107:                for (int i = n - 1; i >= 0; i--) {
1108:                    TimeInstance timeInstance = (TimeInstance) beginInstances
1109:                            .elementAt(i);
1110:                    if (timeInstance instanceof  IntervalTimeInstance) {
1111:                        IntervalTimeInstance iti = (IntervalTimeInstance) timeInstance;
1112:                        if (iti.syncBase.isDescendant(syncTimeContainer)) {
1113:                            beginInstances.removeElementAt(i);
1114:                            iti.dispose();
1115:                        }
1116:                    }
1117:                }
1118:                n = endInstances.size();
1119:                for (int i = n - 1; i >= 0; i--) {
1120:                    TimeInstance timeInstance = (TimeInstance) endInstances
1121:                            .elementAt(i);
1122:                    if (timeInstance instanceof  IntervalTimeInstance) {
1123:                        IntervalTimeInstance iti = (IntervalTimeInstance) timeInstance;
1124:                        if (iti.syncBase.isDescendant(syncTimeContainer)) {
1125:                            endInstances.removeElementAt(i);
1126:                            iti.dispose();
1127:                        }
1128:                    }
1129:                }
1130:            }
1131:
1132:            /**
1133:             * @param parent the time container which might be on the timed element's
1134:             *        parent hierarchy.
1135:             * @return true if this timed element is equal to 'parent' or is a 
1136:             * descendant of 'parent'
1137:             */
1138:            boolean isDescendant(final TimeContainerSupport parent) {
1139:                if (parent == this ) {
1140:                    return true;
1141:                } else if (timeContainer != this ) {
1142:                    return timeContainer.isDescendant(parent);
1143:                } else {
1144:                    return false;
1145:                }
1146:            }
1147:
1148:            /**
1149:             * Dispatches beginEvent. As per the SMIL 2 specification, this dispatches
1150:             * a beginEvent for the resolved begin time, not the observed begin
1151:             * time. It also dispatches any repeat event that might be needed.
1152:             *
1153:             * @param currentTime the current sampling time.
1154:             *
1155:             * @see <a
1156:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-BeginValueSemantics">
1157:             * SMIL2's Begin value semantics</a>
1158:             */
1159:            void dispatchBeginEvent(final Time currentTime) {
1160:                if (animationElement != null) {
1161:                    ModelEvent beginEvent = null;
1162:                    if (seeking) {
1163:                        beginEvent = animationElement.ownerDocument
1164:                                .initEngineEvent(SEEK_BEGIN_EVENT_TYPE,
1165:                                        animationElement);
1166:                    } else {
1167:                        beginEvent = animationElement.ownerDocument
1168:                                .initEngineEvent(BEGIN_EVENT_TYPE,
1169:                                        animationElement);
1170:                    }
1171:
1172:                    eventTime.value = currentInterval.begin.value;
1173:                    beginEvent.eventTime = toRootContainerSimpleTimeClamp(eventTime);
1174:                    animationElement.dispatchEvent(beginEvent);
1175:                }
1176:
1177:                dispatchRepeatEvent(currentTime);
1178:            }
1179:
1180:            /**
1181:             * Dispatches lastDurEndEvent.
1182:             *
1183:             * @see <a
1184:             * href="http://www.w3.org/TR/smil20/smil-timing.html#smil-timing-Timing-BeginEnd-Restart
1185:             * </a>
1186:             */
1187:            void dispatchLastDurEndEvent() {
1188:                if (animationElement != null) {
1189:                    ModelEvent event = animationElement.ownerDocument
1190:                            .initEngineEvent(LAST_DUR_END_EVENT_TYPE,
1191:                                    animationElement);
1192:                    eventTime.value = currentInterval.lastDur.value;
1193:                    event.eventTime = toRootContainerSimpleTimeClamp(eventTime);
1194:                    animationElement.dispatchEvent(event);
1195:                }
1196:            }
1197:
1198:            /**
1199:             * Dispatches seekeEndEvent.
1200:             *
1201:             * @see <a
1202:             * href="http://www.w3.org/TR/smil20/smil-timing.html#smil-timing-Timing-BeginEnd-Restart
1203:             * </a>
1204:             */
1205:            void dispatchSeekEndEvent() {
1206:                if (animationElement != null) {
1207:                    ModelEvent event = animationElement.ownerDocument
1208:                            .initEngineEvent(SEEK_END_EVENT_TYPE,
1209:                                    animationElement);
1210:                    animationElement.dispatchEvent(event);
1211:                }
1212:            }
1213:
1214:            /**
1215:             * Dispatches endEvent. As per the SMIL 2 specification, this dispatches an
1216:             * endEvent for the resolved end time, not the observed end time.
1217:             *
1218:             * @param currentTime the current sampling time.
1219:             *
1220:             * @see <a
1221:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-BeginValueSemantics">
1222:             * SMIL2's Begin value semantics</a>
1223:             */
1224:            void dispatchEndEvent(final Time currentTime) {
1225:                if ((!seeking || state == STATE_PLAYING)
1226:                        && animationElement != null) {
1227:                    ModelEvent endEvent = animationElement.ownerDocument
1228:                            .initEngineEvent(END_EVENT_TYPE, animationElement);
1229:                    if (seeking) {
1230:                        // We are seeking to a new currentTime and the interval
1231:                        // ends during the seek interval and the element was
1232:                        // active at the time of seek. The time for the end
1233:                        // event should be the time _before_ the seek
1234:                        eventTime.value = getRootContainer().lastSampleTime.value;
1235:                        endEvent.eventTime = eventTime;
1236:                    } else {
1237:                        eventTime.value = currentInterval.end.value;
1238:                        endEvent.eventTime = toRootContainerSimpleTimeClamp(eventTime);
1239:                    }
1240:
1241:                    animationElement.dispatchEvent(endEvent);
1242:                }
1243:                dispatchRepeatEvent(currentTime);
1244:            }
1245:
1246:            /**
1247:             * Helper method. 
1248:             *
1249:             * @return this timed element's root container.
1250:             */
1251:            TimeContainerRootSupport getRootContainer() {
1252:                return timeContainer.getRootContainer();
1253:            }
1254:
1255:            /**
1256:             * Dispatches repeatEvent. As per the SMIL 2 specification, this dispatches
1257:             * a repeatEvent for the resolved end time, not the observed repeat
1258:             * time(s).
1259:             *
1260:             * @see <a
1261:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-BeginValueSemantics">
1262:             * SMIL2's Begin value semantics</a>
1263:             */
1264:            void dispatchRepeatEvent(final Time currentTime) {
1265:                if (!seeking && simpleDur.isResolved() && simpleDur.value > 0) {
1266:                    int prevIter = curIter;
1267:
1268:                    Time maxTime = currentTime;
1269:                    if (currentTime.greaterThan(currentInterval.end)) {
1270:                        maxTime = currentInterval.end;
1271:                    }
1272:
1273:                    curIter = (int) ((maxTime.value - currentInterval.begin.value) / simpleDur.value);
1274:
1275:                    if (curIter < 0) {
1276:                        curIter = 0;
1277:                    }
1278:
1279:                    if (maxTime.isSameTime(currentInterval.end)
1280:                            && ((maxTime.value - currentInterval.begin.value)
1281:                                    % simpleDur.value == 0)) {
1282:                        curIter--;
1283:                    }
1284:
1285:                    if (animationElement != null) {
1286:                        for (int i = prevIter; i < curIter; i++) {
1287:                            ModelEvent repeatEvent = animationElement.ownerDocument
1288:                                    .initEngineEvent(REPEAT_EVENT_TYPE,
1289:                                            animationElement);
1290:
1291:                            // The event time is the time at which the repeat happened,
1292:                            // not the time at which it was detected (that would be
1293:                            // currentTime).  So we have to compute the repeat time here
1294:                            Time repeatTime = eventTime;
1295:                            repeatTime.value = currentInterval.begin.value
1296:                                    + (i + 1) * simpleDur.value;
1297:                            repeatTime = toRootContainerSimpleTimeClamp(repeatTime);
1298:                            repeatEvent.eventTime = repeatTime;
1299:                            repeatEvent.repeatCount = i + 1;
1300:                            animationElement.dispatchEvent(repeatEvent);
1301:                        }
1302:                    }
1303:
1304:                    if (prevIter != curIter) {
1305:                        onStartingRepeat(prevIter, curIter);
1306:                    }
1307:                }
1308:            }
1309:
1310:            /**
1311:             * To be overridden by extensions (TimeContainerSupport) to do specific
1312:             * processing when a new iteration is started.
1313:             *
1314:             * @param prevIter the last iteration this element was playing.
1315:             * @param curIter the new iteration this element is playing.
1316:             */
1317:            protected void onStartingRepeat(final int prevIter,
1318:                    final int curIter) {
1319:            }
1320:
1321:            /**
1322:             * Checks if the input interval is non null. If it is, that interval
1323:             * becomes the new currentInterval and the element sets its state 
1324:             * accordingly.
1325:             *
1326:             * @param interval the interval to check.
1327:             * @return true if the candidate interval has become the current interval.
1328:             */
1329:            boolean checkNewInterval(final TimeInterval interval) {
1330:                if (interval == null) {
1331:                    return false;
1332:                } else {
1333:                    currentInterval = interval;
1334:                    simpleDur = computeSimpleDuration(currentInterval.end);
1335:
1336:                    // Transition to the current state
1337:                    // Note that we move to the 'playing' stage only in the
1338:                    // sample method, where event dispatching is done as well.
1339:                    if (previousInterval == null) {
1340:                        state = STATE_WAITING_INTERVAL_0;
1341:                    } else {
1342:                        state = STATE_WAITING_INTERVAL_N;
1343:                    }
1344:
1345:                    // Notify dependents of the new interval
1346:                    dispatchOnNewInterval();
1347:                    return true;
1348:                }
1349:            }
1350:
1351:            /**
1352:             * Helper method to find the active interval, or next interval, for
1353:             * the requested time.
1354:             *
1355:             * @param seekToTime the time we are seeking the active or next interval
1356:             *        for.
1357:             * @return the <code>TimeInterval</code> corresponding to
1358:             *         <code>seekToTime</code>
1359:             */
1360:            TimeInterval seekToInterval(final Time seekToTime) {
1361:                previousInterval = null;
1362:                TimeInterval interval = computeFirstInterval();
1363:                if (interval == null) {
1364:                    return null;
1365:                }
1366:
1367:                while (interval != null && seekToTime.greaterThan(interval.end)) {
1368:                    previousInterval = interval;
1369:                    interval = computeNextInterval();
1370:                }
1371:
1372:                return interval;
1373:            }
1374:
1375:            /**
1376:             * Helper method.
1377:             *
1378:             * @return true if the root time container is flagged as seeking back.
1379:             */
1380:            boolean isSeekingBack() {
1381:                return getRootContainer().seekingBack;
1382:            }
1383:
1384:            /**
1385:             * This method is typically called by this element's time container
1386:             * when it samples.
1387:             *
1388:             * Note that if this element is not in the waiting or playing
1389:             * state, this does nothing. This method assumes that successive
1390:             * calls are made with increasing time values.
1391:             *
1392:             * @param currentTime the time at which this element should be 
1393:             *        sampled. 
1394:             */
1395:            void sample(final Time currentTime) {
1396:                // Handle state transitions if there is a current interval
1397:                boolean endOfInterval = false;
1398:                boolean seekBack = false;
1399:
1400:                switch (state) {
1401:                default:
1402:                    return;
1403:                case STATE_FILL:
1404:                    if (seeking && isSeekingBack()) {
1405:                        seekBack = true;
1406:                    }
1407:                    break;
1408:                case STATE_WAITING_INTERVAL_0:
1409:                    if (currentTime.greaterThan(currentInterval.begin)) {
1410:                        // This element has begun between the previous sampling
1411:                        // and now. Dispatch the beginEvent.
1412:                        dispatchBeginEvent(currentTime);
1413:
1414:                        if (currentTime.greaterThan(currentInterval.end)) {
1415:                            // This element has also ended between the 
1416:                            // previous sampling and now. Dispatch the endEvent
1417:                            dispatchLastDurEndEvent();
1418:                            dispatchEndEvent(currentTime);
1419:                            endOfInterval = true;
1420:                        }
1421:
1422:                        state = STATE_PLAYING;
1423:                        playFill = false;
1424:                    }
1425:                    break;
1426:                case STATE_WAITING_INTERVAL_N:
1427:                    if (currentTime.greaterThan(currentInterval.begin)) {
1428:                        // This element has begun between the previous sampling
1429:                        // and now. Dispatch the beginEvent.
1430:                        dispatchBeginEvent(currentTime);
1431:
1432:                        if (currentTime.greaterThan(currentInterval.end)) {
1433:                            // This element has also ended between the 
1434:                            // previous sampling and now. Dispatch the endEvent
1435:                            dispatchLastDurEndEvent();
1436:                            dispatchEndEvent(currentTime);
1437:                            endOfInterval = true;
1438:                        }
1439:
1440:                        state = STATE_PLAYING;
1441:                        playFill = false;
1442:                    } else {
1443:                        if (seeking && isSeekingBack()) {
1444:                            seekBack = true;
1445:                        }
1446:                    }
1447:                    break;
1448:                case STATE_PLAYING:
1449:                    if (currentTime.greaterThan(currentInterval.lastDur)) {
1450:                        if (!playFill) {
1451:                            dispatchLastDurEndEvent();
1452:                            playFill = true;
1453:                        }
1454:                    }
1455:
1456:                    if (currentTime.greaterThan(currentInterval.end)) {
1457:                        // This element has also ended between the 
1458:                        // previous sampling and now. Dispatch the endEvent
1459:                        dispatchEndEvent(currentTime);
1460:                        endOfInterval = true;
1461:                    } else {
1462:                        if (!seeking) {
1463:                            dispatchRepeatEvent(currentTime);
1464:                        } else if (currentInterval.begin
1465:                                .greaterThan(currentTime)) {
1466:                            // We are seeking backwards and the current interval
1467:                            // began during the seek interval. We need to 
1468:                            // dispatch and end event at the time before the 
1469:                            // seek.
1470:                            dispatchLastDurEndEvent();
1471:                            dispatchEndEvent(currentTime);
1472:                            endOfInterval = true;
1473:                            seekBack = true;
1474:                        }
1475:                    }
1476:                    break;
1477:                }
1478:
1479:                // If we just finished an interval,
1480:                // we need to compute the new interval now
1481:                if (endOfInterval) {
1482:                    previousInterval = currentInterval;
1483:                    currentInterval = null;
1484:
1485:                    // If we cannot restart, we just go to the fill state
1486:                    if (restart == RESTART_NEVER) {
1487:                        state = STATE_FILL;
1488:                        playFill = false;
1489:                    } else {
1490:                        TimeInterval nextInterval = null;
1491:                        if (!seekBack) {
1492:                            nextInterval = computeNextInterval();
1493:                        } else {
1494:                            nextInterval = seekToInterval(currentTime);
1495:                        }
1496:
1497:                        if (!checkNewInterval(nextInterval)) {
1498:                            // There is no next interval, we move to the 
1499:                            // fill state.
1500:                            state = STATE_FILL;
1501:                            playFill = false;
1502:                        }
1503:                    }
1504:
1505:                    // This recursive call is needed in case we sample so far ahead 
1506:                    // that we are skipping several intervals.
1507:                    // For example if the intervals are:
1508:                    // [0, 1000[
1509:                    // [2000, 3000[
1510:                    // [5000, 6500[
1511:                    // [8000, 1000[
1512:                    // and we sample at 0 and then 7000
1513:                    sample(currentTime);
1514:                    return;
1515:                } else if (seekBack) {
1516:                    // If we are in the FILL state and we cannot restart, then we stay
1517:                    // in FILL state if seeking to after the previous interval's
1518:                    // end. Otherwise, we move to the STATE_NO_INTERVAL state.
1519:                    if (state == STATE_FILL && restart == RESTART_NEVER) {
1520:                        if (previousInterval.end.greaterThan(currentTime)) {
1521:                            dispatchSeekEndEvent();
1522:                            state = STATE_NO_INTERVAL;
1523:                            playFill = false;
1524:                            return;
1525:                        }
1526:                    } else {
1527:                        TimeInterval seekInterval = seekToInterval(currentTime);
1528:
1529:                        // If the seek interval is the same as the current interval, we
1530:                        // do not need to do further sampling because we were in, or
1531:                        // have already moved to, the right interval.
1532:                        if (seekInterval == null
1533:                                || currentInterval == null
1534:                                || !(seekInterval.begin
1535:                                        .isSameTime(currentInterval.begin) && seekInterval.end
1536:                                        .isSameTime(currentInterval.end))) {
1537:
1538:                            // We need to check if there is an interval active at the
1539:                            // time we are seeking to.
1540:                            if (!checkNewInterval(seekInterval)) {
1541:                                // There is no currentInterval. 
1542:                                // Switch to the right state 
1543:                                if (previousInterval != null) {
1544:                                    state = STATE_FILL;
1545:                                    playFill = false;
1546:                                } else {
1547:                                    dispatchSeekEndEvent();
1548:                                    state = STATE_NO_INTERVAL;
1549:                                    playFill = false;
1550:                                    return;
1551:                                }
1552:                            } else {
1553:                                // There is an interval at the time we are 
1554:                                // seeking to. End the current interval (which
1555:                                // we know is different because of the previous if
1556:                                // statement) and sample again to have the new current
1557:                                // interval kick-in.
1558:                                dispatchSeekEndEvent();
1559:                                sample(currentTime);
1560:                                return;
1561:                            }
1562:                        }
1563:                    }
1564:                }
1565:
1566:                // 
1567:                // Different behaviors depending on the state. At this point, we
1568:                // should only be in one of the following states:
1569:                // - STATE_FILL
1570:                // - STATE_PLAYING
1571:                // - STATE_WAITING_INTERVAL_0
1572:                // - STATE_WAITING_INTERVAL_N
1573:                // 
1574:                long localTime = 0;
1575:                switch (state) {
1576:                default:
1577:                    // This should _never_ happen, given this method's code.
1578:                    // This is extremely hard to test (would require changing
1579:                    // state during the execution of this method) and does 
1580:                    // not need to be covered by the test suite. 
1581:                    throw new IllegalStateException();
1582:                case STATE_WAITING_INTERVAL_N:
1583:                case STATE_FILL:
1584:                    if (fillBehavior == FILL_BEHAVIOR_FREEZE) {
1585:                        // If we are in STATE_WAITING_INTERVAL_N or STATE_FILL,
1586:                        // it means that we had a previous interval. Therefore,
1587:                        // previousInterval is guaranteed to be not null.
1588:                        localTime = previousInterval.lastDur.value
1589:                                - previousInterval.begin.value;
1590:                    } else {
1591:                        return;
1592:                    }
1593:                    break;
1594:                case STATE_WAITING_INTERVAL_0:
1595:                    return;
1596:                case STATE_PLAYING:
1597:                    // If we are still in the PLAYING state but we are past the
1598:                    // end of the last simple duration end, make sure we sample at
1599:                    // that last simple duration time.
1600:                    if (!playFill) {
1601:                        localTime = currentTime.value
1602:                                - currentInterval.begin.value;
1603:                    } else {
1604:                        localTime = currentInterval.lastDur.value
1605:                                - currentInterval.begin.value;
1606:                    }
1607:                    break;
1608:                }
1609:
1610:                // If we get to this point, it means we were able to
1611:                // compute a localTime we need to sample at. We are
1612:                // either playing the animation or we are in a frozen
1613:                // state. Compute the simple time from the localTime.
1614:                if (simpleDur.isResolved()) {
1615:                    localTime = localTime % simpleDur.value;
1616:                    if (state != STATE_PLAYING || playFill) {
1617:                        // If we are not playing, it means we are frozen
1618:                        // (either STATE_WAITING_N or STATE_FILL).
1619:                        //
1620:                        // Make sure we sample on the end of the simple time 
1621:                        // if we froze at the end of the interval.
1622:                        // 
1623:                        // Note that we do not need to test if the value
1624:                        // was 0 before %. Because we are _not_ playing, it
1625:                        // means we are _not_ on the first value of the 
1626:                        // interval, therefore, we never run into the condition
1627:                        // where localTime would be 0 before % and therefore,
1628:                        // we do not need to test for that condition
1629:                        //
1630:                        if (localTime == 0) {
1631:                            // Freeze on the last value in the simple duration
1632:                            // interval only when we freeze.
1633:                            localTime = simpleDur.value;
1634:                        }
1635:                    }
1636:                }
1637:
1638:                // At this point, we have computed our simple time.
1639:                // Ready to connect with the animation engine.
1640:                sampleAt(localTime);
1641:
1642:                // Keep the last simpleTime this element was sampled at
1643:                lastSampleTime = localTime;
1644:            }
1645:
1646:            /**
1647:             * Samples this element at the given simple time.
1648:             * Should be overridden for specific behaviors.
1649:             *
1650:             * @param simpleTime this timed element simple time.
1651:             */
1652:            void sampleAt(final long simpleTime) {
1653:            }
1654:
1655:            /**
1656:             * Called when this element is the target of an hyperlink. The 
1657:             * behavior is that defined in the SMIL 2 specification.
1658:             * 
1659:             * @see <a
1660:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-HyperlinksAndTiming">
1661:             * SMIL 2 Specification</a>
1662:             */
1663:            public void activate() {
1664:                // 1. If the target element is active, seek the document time back to
1665:                // the begin time of the current interval for the element.
1666:                if (state == STATE_PLAYING) {
1667:                    Time seekToTime = new Time(currentInterval.begin.value);
1668:                    seekToTime = toRootContainerSimpleTimeClamp(seekToTime);
1669:
1670:                    if (seekToTime.isResolved()) {
1671:                        seekTo(seekToTime);
1672:                        return;
1673:                    }
1674:                }
1675:
1676:                //
1677:                // 2. Else if the target element begin time is resolved (i.e. there is
1678:                // at least one interval defined for the element), seek the document
1679:                // time (forward or back, as needed) to the begin time of the first
1680:                // interval for the target element. Note that the begin time may be
1681:                // resolved as a result of an earlier hyperlink, DOM or event
1682:                // activation. Once the begin time is resolved (and until the element is
1683:                // reset, e.g. when the parent repeats), hyperlink traversal always
1684:                // seeks. For a discussion of "reset", see Resetting element state. Note
1685:                // also that for an element begin to be resolved, the begin time of all
1686:                // ancestor elements must also be resolved.
1687:                //
1688:                TimeInterval firstInterval = computeFirstInterval();
1689:                if (firstInterval != null) {
1690:                    Time seekToTime = new Time(firstInterval.begin.value);
1691:
1692:                    seekToTime = toRootContainerSimpleTimeClamp(seekToTime);
1693:
1694:                    if (seekToTime.isResolved()) {
1695:                        seekTo(seekToTime);
1696:                        return;
1697:                    }
1698:                }
1699:
1700:                // 3. Else (i.e. there are no defined intervals for the element), the
1701:                // target element begin time must be resolved. This may require seeking
1702:                // and/or resolving ancestor elements as well. This is done by recursing
1703:                // from the target element up to the closest ancestor element that has a
1704:                // resolved begin time (again noting that for an element to have a
1705:                // resolved begin time, all of its ancestors must have resolved begin
1706:                // times). Then, the recursion is "unwound", and for each ancestor in
1707:                // turn (beneath the resolved ancestor) as well as the target element,
1708:                // the following steps are performed:
1709:                //
1710:                //     1. If the element begin time is resolved, seek the document time
1711:                //     (forward or back, as needed) to the begin time of the first
1712:                //     interval for the target element.
1713:                //
1714:                //     2. Else (if the begin time is not resolved), just resolve the
1715:                //     element begin time at the current time on its parent time
1716:                //     container (given the current document position). Disregard the
1717:                //     sync-base or event base of the element, and do not
1718:                //     "back-propagate" any timing logic to resolve the element, but
1719:                //     rather treat it as though it were defined with begin="indefinite"
1720:                //     and just resolve begin time to the current parent time. This
1721:                //     should create an interval and propagate to time dependents.
1722:                //
1723:                seekTo(Time.UNRESOLVED);
1724:            }
1725:
1726:            /**
1727:             * Implementation helper method for seekTo behavior.
1728:             *
1729:             * @param seekToTime the time to seek to. 
1730:             */
1731:            void seekTo(final Time seekToTime) {
1732:                if (seekToTime.isResolved()) {
1733:                    // Now, move up the container graph.
1734:                    timeContainer.seekTo(seekToTime);
1735:                } else {
1736:                    // 3. Else (i.e. there are no defined intervals for the element),
1737:                    // the target element begin time must be resolved. This may require
1738:                    // seeking and/or resolving ancestor elements as well. This is done
1739:                    // by recursing from the target element up to the closest ancestor
1740:                    // element that has a resolved begin time (again noting that for an
1741:                    // element to have a resolved begin time, all of its ancestors must
1742:                    // have resolved begin times).
1743:                    if (timeContainer.state != STATE_PLAYING) {
1744:                        TimeInterval firstContainerInterval = timeContainer
1745:                                .computeFirstInterval();
1746:                        if (firstContainerInterval == null) {
1747:                            timeContainer.seekTo(Time.UNRESOLVED);
1748:                        } else {
1749:                            Time parentSeekTo = new Time(
1750:                                    firstContainerInterval.begin.value);
1751:                            parentSeekTo = timeContainer
1752:                                    .toRootContainerSimpleTimeClamp(parentSeekTo);
1753:                            timeContainer.seekTo(parentSeekTo);
1754:                        }
1755:                    }
1756:
1757:                    // Then, the recursion is "unwound", and for each ancestor in turn
1758:                    // (beneath the resolved ancestor) as well as the target element,
1759:                    // the following steps are performed:
1760:                    //
1761:                    //     1. If the element begin time is resolved, seek the document
1762:                    //     time (forward or back, as needed) to the begin time of the
1763:                    //     first interval for the target element.
1764:                    //
1765:                    //     2. Else (if the begin time is not resolved), just resolve the
1766:                    //     element begin time at the current time on its parent time
1767:                    //     container (given the current document position). Disregard
1768:                    //     the sync-base or event base of the element, and do not
1769:                    //     "back-propagate" any timing logic to resolve the element, but
1770:                    //     rather treat it as though it were defined with
1771:                    //     begin="indefinite" and just resolve begin time to the current
1772:                    //     parent time. This should create an interval and propagate to
1773:                    //     time dependents.
1774:                    //
1775:                    TimeInterval firstInterval = computeFirstInterval();
1776:                    if (firstInterval == null) {
1777:                        begin();
1778:                        firstInterval = computeFirstInterval();
1779:                    }
1780:
1781:                    Time goToTime = new Time(firstInterval.begin.value);
1782:                    if (goToTime.value < 0) {
1783:                        goToTime.value = 0;
1784:                    }
1785:
1786:                    timeContainer
1787:                            .seekTo(toRootContainerSimpleTimeClamp(goToTime));
1788:                }
1789:            }
1790:
1791:            /**
1792:             * Should be called whenever there is a change in the begin time instance 
1793:             * list.
1794:             */
1795:            private void reEvaluateBeginTime() {
1796:                switch (state) {
1797:                case STATE_WAITING_INTERVAL_0: {
1798:                    TimeInterval curInt = computeFirstInterval();
1799:                    if (curInt != null) {
1800:                        currentInterval.setBegin(curInt.begin);
1801:                        currentInterval.setEnd(curInt.end);
1802:                        computeLastDur(currentInterval);
1803:                    } else {
1804:                        // There no first interval any more, after
1805:                        // the begin instance has been changed.
1806:                        // We move back to the no interval state
1807:                        pruneCurrentInterval();
1808:                    }
1809:                }
1810:                    break;
1811:                case STATE_WAITING_INTERVAL_N: {
1812:                    TimeInterval curInt = computeNextInterval();
1813:                    if (curInt != null) {
1814:                        currentInterval.setBegin(curInt.begin);
1815:                        currentInterval.setEnd(curInt.end);
1816:                        computeLastDur(currentInterval);
1817:                    } else {
1818:                        pruneCurrentInterval();
1819:                    }
1820:                }
1821:
1822:                default:
1823:                    return;
1824:                }
1825:            }
1826:
1827:            /**
1828:             * Recomputes the end time. This only does something if the 
1829:             * element is playing or waiting to play, because it may update
1830:             * the currentInterval's end time.
1831:             */
1832:            void reEvaluateEndTime() {
1833:                switch (state) {
1834:                case STATE_WAITING_INTERVAL_0:
1835:                case STATE_WAITING_INTERVAL_N:
1836:                case STATE_PLAYING:
1837:                    // We are dealing with a new end time and the animation is
1838:                    // playing. Re-evaluate the end time and update the 
1839:                    // current interval.
1840:                    //
1841:                    // Note that we cannot simply do a computeNextInterval as this
1842:                    // might yield a begin time different from the one on the 
1843:                    // playing interval.
1844:                    //
1845:                    Time newEnd = computeEndTime(currentInterval.begin);
1846:
1847:                    // It is unclear what should happen here if newEnd is null.
1848:                    // This could happen if a list of end times was empty and
1849:                    // a SyncBaseCondition, for example, inserts a new end instance
1850:                    // that is _before_ the currentInterval's begin time. In that
1851:                    // situation, should we just ignore newEnd or should we 
1852:                    // prune the currentInterval (if WAITING_0 or WAITING_N) and
1853:                    // stop it (if PLAYING)?
1854:                    //
1855:                    // Pending further investigation of this corner case, we simply
1856:                    // ignore a null newEnd
1857:                    if (newEnd != null) {
1858:                        currentInterval.setEnd(newEnd);
1859:                        computeLastDur(currentInterval);
1860:                    }
1861:                    break;
1862:                default:
1863:                    break;
1864:                }
1865:            }
1866:
1867:            /**
1868:             * Helper method invoked when the current interval becomes invalid
1869:             * after re-evaluation of the begin and end instances. 
1870:             *
1871:             * @see <a href="file:///D:/work/doc/specs/smil20.html#smil-timing-q86">
1872:             *      SMIL 2: Principles for building and pruning intervals</a>
1873:             */
1874:            private void pruneCurrentInterval() {
1875:                TimeInterval prunedInterval = currentInterval;
1876:                currentInterval = null;
1877:                if (previousInterval != null) {
1878:                    state = STATE_FILL;
1879:                } else {
1880:                    state = STATE_NO_INTERVAL;
1881:                }
1882:
1883:                prunedInterval.prune();
1884:            }
1885:
1886:            /**
1887:             * This method inserts the input time instance in the instance list
1888:             *
1889:             * @param newInstance the instance to reposition in its instance list
1890:             * @see #addTimeInstance
1891:             * @see #onTimeInstanceUpdate
1892:             */
1893:            private void insertTimeInstance(final TimeInstance newInstance) {
1894:                Vector instances = beginInstances;
1895:                if (!newInstance.isBegin) {
1896:                    instances = endInstances;
1897:                }
1898:
1899:                int n = instances.size();
1900:                int i = 0;
1901:                Time newTime = newInstance.time;
1902:                for (i = 0; i < n; i++) {
1903:                    TimeInstance timeInstance = (TimeInstance) instances
1904:                            .elementAt(i);
1905:                    if (!newTime.greaterThan(timeInstance.time)) {
1906:                        instances.insertElementAt(newInstance, i);
1907:                        break;
1908:                    }
1909:                }
1910:
1911:                if (i == n) {
1912:                    instances.addElement(newInstance);
1913:                }
1914:            }
1915:
1916:            /**
1917:             * Called to add a new <code>TimeInstance</code> to the begin or end
1918:             * instance list (depending on the new instance's <code>isBegin</code>
1919:             * setting).
1920:             *
1921:             * This is also called as a result of invoking beginAt or endAt.
1922:             *
1923:             * @param newInstance the new <code>TimeInstance</code> to add to the 
1924:             *        list. Should not be null.
1925:             * @see #beginAt
1926:             * @see #endAt
1927:             * @see <a
1928:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-BeginEnd-Restart">
1929:             * SMIL 2, Interaction with restart semantic</a>
1930:             */
1931:            void addTimeInstance(final TimeInstance newInstance) {
1932:                if (timingUpdate) {
1933:                    // We are in a timing dependency cycle. Break now.
1934:                    return;
1935:                }
1936:
1937:                timingUpdate = true;
1938:
1939:                try {
1940:                    insertTimeInstance(newInstance);
1941:
1942:                    // Do not handle instance time list changes if we are 
1943:                    // initializing the time graph.
1944:                    if (state == STATE_PRE_INIT) {
1945:                        return;
1946:                    }
1947:
1948:                    if (!newInstance.isBegin) {
1949:                        reEvaluateEndTime();
1950:                        return;
1951:                    }
1952:
1953:                    // Impact the current interval if needed
1954:                    switch (state) {
1955:                    case STATE_NO_INTERVAL:
1956:                        checkNewInterval(computeFirstInterval());
1957:                        break;
1958:                    case STATE_FILL:
1959:                        if (restart != RESTART_NEVER) {
1960:                            checkNewInterval(computeNextInterval());
1961:                        }
1962:                        // Else, don't do anything: this element cannot
1963:                        // restart.
1964:                        break;
1965:                    case STATE_WAITING_INTERVAL_0: {
1966:                        TimeInterval curInt = computeFirstInterval();
1967:                        if (curInt != null) {
1968:                            currentInterval.setBegin(curInt.begin);
1969:                            currentInterval.setEnd(curInt.end);
1970:                            computeLastDur(currentInterval);
1971:                        }
1972:                    }
1973:                        break;
1974:                    case STATE_WAITING_INTERVAL_N: {
1975:                        TimeInterval curInt = computeNextInterval();
1976:                        // We just added a new time instance. It is not possible
1977:                        // that adding a time instance would make 
1978:                        // computeNextInterval return a null interval here. 
1979:                        // So we do not test for a null condition because it 
1980:                        // can't happen. If it happens,
1981:                        // it means there is a bug in computeNextInterval or that
1982:                        // the beginInstance list was messed with, which should not
1983:                        // happen.
1984:                        currentInterval.setBegin(curInt.begin);
1985:                        currentInterval.setEnd(curInt.end);
1986:                        computeLastDur(currentInterval);
1987:                    }
1988:                        break;
1989:                    case STATE_PLAYING:
1990:                        if (newInstance.isBegin) {
1991:                            if (restart == RESTART_ALWAYS) {
1992:                                if (currentInterval.end
1993:                                        .greaterThan(newInstance.time)
1994:                                        && newInstance.time
1995:                                                .greaterThan(currentInterval.begin)) {
1996:                                    // Update the current interval end time with this
1997:                                    // new begin time.
1998:                                    currentInterval.setEnd(newInstance.time);
1999:                                    computeLastDur(currentInterval);
2000:
2001:                                    // Next interval will be computed in the next call
2002:                                    // to sample();
2003:                                }
2004:                                // Ignore the new instance if it if falls after or
2005:                                // before the current interval
2006:                            }
2007:                            // Ignore the new instance if we cannot restart this
2008:                            // animation.
2009:                        }
2010:                        break;
2011:                    default:
2012:                        throw new IllegalStateException();
2013:                    }
2014:                } finally {
2015:                    timingUpdate = false;
2016:                }
2017:            }
2018:
2019:            /**
2020:             * Called by <code>TimeInstance</code>s when they are updated.
2021:             * In response, the TimedElement may recompute its current
2022:             * interval. 
2023:             *
2024:             * @param timeInstance the <code>TimeInstance</code> that was just 
2025:             *        updated.
2026:             *
2027:             * @throws NullPointerException if timeInstance is null.
2028:             *
2029:             * @see <a
2030:             * href="http://www.w3.org/TR/smil20/smil-timing.html#Timing-SemanticsOfTimingModel">
2031:             * SMIL 2: Building the instance times lists</a>
2032:             */
2033:            void onTimeInstanceUpdate(final TimeInstance timeInstance) {
2034:                if (timingUpdate) {
2035:                    // We are in a timing dependency cycle, break now.
2036:                    return;
2037:                }
2038:
2039:                timingUpdate = true;
2040:
2041:                try {
2042:                    // First, reposition the instance in the instance list
2043:                    if (timeInstance.isBegin) {
2044:                        beginInstances.removeElement(timeInstance);
2045:                    } else {
2046:                        endInstances.removeElement(timeInstance);
2047:                    }
2048:                    insertTimeInstance(timeInstance);
2049:
2050:                    if (state == STATE_PRE_INIT) {
2051:                        return;
2052:                    }
2053:
2054:                    if (timeInstance.isBegin) {
2055:                        // We are dealing with a begin instance change.
2056:                        reEvaluateBeginTime();
2057:                    } else {
2058:                        // We are dealing with an end time instance.
2059:                        reEvaluateEndTime();
2060:                    }
2061:                } finally {
2062:                    timingUpdate = false;
2063:                }
2064:            }
2065:
2066:            /**
2067:             * Called to remove a <code>TimeInstance</code> when a 
2068:             * <code>TimeInterval</code> is pruned. This causes a re-evaluation
2069:             * of the current interval's begin or end time.
2070:             *
2071:             * @param timeInstance the instance to remove from its instance list.
2072:             */
2073:            void removeTimeInstance(final TimeInstance timeInstance) {
2074:                if (timeInstance.isBegin) {
2075:                    beginInstances.removeElement(timeInstance);
2076:                } else {
2077:                    endInstances.removeElement(timeInstance);
2078:                }
2079:
2080:                if (timingUpdate) {
2081:                    return;
2082:                }
2083:
2084:                timingUpdate = true;
2085:                try {
2086:                    if (state != STATE_PRE_INIT) {
2087:                        if (timeInstance.isBegin) {
2088:                            reEvaluateBeginTime();
2089:                        } else {
2090:                            reEvaluateEndTime();
2091:                        }
2092:                    }
2093:                } finally {
2094:                    timingUpdate = false;
2095:                }
2096:            }
2097:
2098:            /**
2099:             * Called by <code>TimeCondition</code>s when they are constructed.
2100:             *
2101:             * @param newCondition the new <code>TimeCondition</code> to add to the 
2102:             *        list. Should not be null.
2103:             */
2104:            void addTimeCondition(final TimeCondition newCondition) {
2105:                if (newCondition.isBegin) {
2106:                    beginConditions.addElement(newCondition);
2107:                } else {
2108:                    endConditions.addElement(newCondition);
2109:                }
2110:            }
2111:
2112:            /**
2113:             * @param eventCondition the <code>EventBaseCondition</code> to check
2114:             *        against.
2115:             * @return true if this <code>TimedElement</code> has a begin condition
2116:             *         corresponding to the input <code>EventBaseCondition</code>
2117:             */
2118:            boolean hasBeginCondition(final EventBaseCondition eventCondition) {
2119:                int n = beginConditions.size();
2120:                for (int i = 0; i < n; i++) {
2121:                    TimeCondition condition = (TimeCondition) beginConditions
2122:                            .elementAt(i);
2123:                    if (condition instanceof  EventBaseCondition) {
2124:                        EventBaseCondition beginCondition = (EventBaseCondition) condition;
2125:                        if (beginCondition.eventType
2126:                                .equals(eventCondition.eventType)
2127:                                && beginCondition.eventBase == eventCondition.eventBase) {
2128:                            return true;
2129:                        }
2130:
2131:                    }
2132:                }
2133:
2134:                return false;
2135:            }
2136:
2137:            /**
2138:             * @return this <code>TimedElement</code>'s current time.
2139:             */
2140:            Time getCurrentTime() {
2141:                return timeContainer.getSimpleTime();
2142:            }
2143:
2144:            /**
2145:             * Converts the input 'local' time to an absolute wallclock time.
2146:             *
2147:             * @param localTime the time to convert to wallclock time
2148:             * @return the time converted to an absolute wallclock time.
2149:             */
2150:            long toWallClockTime(final long localTime) {
2151:                return timeContainer.toWallClockTime(localTime);
2152:            }
2153:
2154:            /**
2155:             * Converts the input simple time (i.e., a time in the parent container's
2156:             * simple duration) to a root container simple time (i.e., a time
2157:             * in the root time container's simple time interval). Note that this method
2158:             * mutates the input time value. If the associated time container does not 
2159:             * have a current interval, this method returns Time.UNRESOLVED.
2160:             *
2161:             * @param simpleTime the time in the parent container's simple duration.
2162:             *        Should not be null. If simpleTime is Time.UNRESOLVED, the returned
2163:             *        time is UNRESOLVED. If simpleTime is Time.INDEFINITE, the returned
2164:             *        time is INDEFINITE.
2165:             * @return a time in the root time container's simple duration (i.e., in 
2166:             *         the root container's simple time interval).
2167:             */
2168:            Time toRootContainerSimpleTime(Time simpleTime) {
2169:                if (timeContainer.currentInterval == null) {
2170:                    return Time.UNRESOLVED;
2171:                }
2172:
2173:                if (!simpleTime.isResolved()) {
2174:                    return simpleTime;
2175:                }
2176:
2177:                // Convert to the container's container simple time
2178:
2179:                // Account for repeat behavior. This yields a time
2180:                // in the [0, ActiveTime[ time system
2181:                if (timeContainer.simpleDur.isResolved()) {
2182:                    simpleTime.value += timeContainer.curIter
2183:                            * timeContainer.simpleDur.value;
2184:                }
2185:
2186:                // By adding the timeContainer's begin value, we are
2187:                // translating the simpleTime to a time in the container's
2188:                // container simple duration [0, simpleDur[ interval.
2189:                simpleTime.value += timeContainer.currentInterval.begin.value;
2190:
2191:                // Now, move the conversion one level up.
2192:                return timeContainer.toRootContainerSimpleTime(simpleTime);
2193:            }
2194:
2195:            /**
2196:             * Same definition and behavior as toRootContainerSimpleTime, except that
2197:             * this method clamps values to the container's simple duration. This 
2198:             * means that a value smaller than zero is converted to 0 and a value
2199:             * greater than the simple duration is reduced to the simple duration.
2200:             *
2201:             * @param simpleTime the time in the parent container's simple duration.
2202:             *        Should not be null. If simpleTime is Time.UNRESOLVED, the returned
2203:             *        time is UNRESOLVED. If simpleTime is Time.INDEFINITE, the returned
2204:             *        time is INDEFINITE.
2205:             * @return a time in the root time container's simple duration (i.e., in 
2206:             *         the root container's simple time interval).
2207:             */
2208:            Time toRootContainerSimpleTimeClamp(final Time simpleTime) {
2209:                if (timeContainer.currentInterval == null) {
2210:                    return Time.UNRESOLVED;
2211:                }
2212:
2213:                if (!simpleTime.isResolved()) {
2214:                    return simpleTime;
2215:                }
2216:
2217:                // Clamp to zero.
2218:                if (simpleTime.value < 0) {
2219:                    simpleTime.value = 0;
2220:                }
2221:
2222:                // Convert to the container's _container_ simple time
2223:
2224:                // Account for repeat behavior. This yields a time
2225:                // in the [0, ActiveTime[ time system
2226:                if (timeContainer.simpleDur.isResolved()) {
2227:                    // Clamp to the simple duration.
2228:                    if (simpleTime.value > timeContainer.simpleDur.value) {
2229:                        simpleTime.value = timeContainer.simpleDur.value;
2230:                    }
2231:                    simpleTime.value += timeContainer.curIter
2232:                            * timeContainer.simpleDur.value;
2233:                } else {
2234:                    // Still clamp to the container's active duration.
2235:                    if (timeContainer.currentInterval.end.isResolved()) {
2236:                        long maxDur = timeContainer.currentInterval.end.value
2237:                                - timeContainer.currentInterval.begin.value;
2238:                        if (simpleTime.value > maxDur) {
2239:                            simpleTime.value = maxDur;
2240:                        }
2241:                    }
2242:                }
2243:
2244:                // By adding the timeContainer's begin value, we are
2245:                // translating the simpleTime to a time in the container's
2246:                // container simple duration [0, simpleDur[ interval.
2247:                simpleTime.value += timeContainer.currentInterval.begin.value;
2248:
2249:                // Now, move the conversion one level up.
2250:                return timeContainer.toRootContainerSimpleTimeClamp(simpleTime);
2251:            }
2252:
2253:            /**
2254:             * Converts the input root container simple time (i.e., a time in the root
2255:             * container's simple time interval) to a time in this element's time
2256:             * container simple duration. Note that this method mutates the input
2257:             * argument. If the associated time container does not 
2258:             * have a current interval, this method returns Time.UNRESOLVED.
2259:             *
2260:             * @param simpleTime the time in the root container's simple duration.  If
2261:             * simpleTime is Time.UNRESOLVED, the returned time is UNRESOLVED. If
2262:             * simpleTime is Time.INDEFINITE, the returned time is INDEFINITE.
2263:             *
2264:             * @return a simple time in the parent container's simple duration The
2265:             * return value is in the [0, container simple duration] interval.
2266:             */
2267:            Time toContainerSimpleTime(Time simpleTime) {
2268:                if (timeContainer.currentInterval == null) {
2269:                    return Time.UNRESOLVED;
2270:                }
2271:
2272:                if (!simpleTime.isResolved()) {
2273:                    return simpleTime;
2274:                }
2275:
2276:                // Convertion to the container's container simple time
2277:                simpleTime = timeContainer.toContainerSimpleTime(simpleTime);
2278:
2279:                // Account for the container's begin value
2280:                simpleTime.value -= timeContainer.currentInterval.begin.value;
2281:
2282:                // Account for repeat behavior
2283:                if (timeContainer.simpleDur.isResolved()) {
2284:                    simpleTime.value -= timeContainer.curIter
2285:                            * timeContainer.simpleDur.value;
2286:                }
2287:
2288:                return simpleTime;
2289:            }
2290:
2291:            /**
2292:             * @return the container's simple duration.
2293:             */
2294:            Time getContainerSimpleDuration() {
2295:                return timeContainer.simpleDur;
2296:            }
2297:
2298:            /**
2299:             * Debug helper.
2300:             * @return a description of this object.
2301:             */
2302:            public String toString() {
2303:                String str = "[Animation: " + animationElement + "] ["
2304:                        + getStateString() + "]";
2305:                switch (state) {
2306:                case STATE_WAITING_INTERVAL_N:
2307:                case STATE_WAITING_INTERVAL_0:
2308:                case STATE_PLAYING:
2309:                    str += "[" + currentInterval.begin + ", "
2310:                            + currentInterval.end
2311:                            /*
2312:                            + ", "
2313:                            + currentInterval.lastDur
2314:                             */
2315:                            + "]";
2316:                    break;
2317:                default:
2318:                    break;
2319:                }
2320:                return str;
2321:            }
2322:
2323:            /**
2324:             * Debug helper. Converts the state to a string.
2325:             *
2326:             * @return a string describing the element's state.
2327:             */
2328:            String getStateString() {
2329:                switch (state) {
2330:                case STATE_PRE_INIT:
2331:                    return "PRE_INIT";
2332:                case STATE_WAITING_INTERVAL_0:
2333:                    return "WAITING_INTERVAL_0";
2334:                case STATE_WAITING_INTERVAL_N:
2335:                    return "WAITING_INTERVAL_N";
2336:                case STATE_NO_INTERVAL:
2337:                    return "NO_INTERVAL";
2338:                case STATE_FILL:
2339:                    return "FILL";
2340:                case STATE_PLAYING:
2341:                    return "PLAYING";
2342:                default:
2343:                    return "UNKNOWN";
2344:                }
2345:            }
2346:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.