001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.perseus.model;
027:
028: import org.w3c.dom.events.Event;
029: import org.w3c.dom.events.EventListener;
030:
031: /**
032: * An <code>EventBaseCondition</code> generates a <code>TimeInstance</code>
033: * everytime the associated event happens.
034: *
035: * <p>It is the responsibility of this class to register as an
036: * <code>EventListener</code>. The <code>TimeInstance</code> created
037: * by an <code>EventBaseCondition</code> are cleared on reset, i.e, when
038: * the associated <code>TimedElementSupport</code> restarts.
039: *
040: * @version $Id: EventBaseCondition.java,v 1.3 2006/06/29 10:47:31 ln156897 Exp $
041: */
042: public class EventBaseCondition extends TimeCondition implements
043: EventListener, IDRef {
044: /**
045: * Offset from the event base
046: */
047: long offset;
048:
049: /**
050: * Keeps a reference to the last occurence of the event.
051: */
052: Time lastEventTime = Time.UNRESOLVED;
053:
054: /**
055: * The event base, i.e., the element which generates events this
056: * listeners listens to.
057: */
058: ModelNode eventBase;
059:
060: /**
061: * The id of the event base. If null, the event base is the timed
062: * element of this condition.
063: */
064: String eventBaseId;
065:
066: /**
067: * The type of event this listener listens to.
068: */
069: String eventType;
070:
071: /**
072: * @param timedElement the associated <code>TimedElementSupport</code>.
073: * Should not be null.
074: * @param isBegin defines whether this condition is for a begin list.
075: * @param eventBaseId the id of the element which generates events this
076: * listener listens to. If null, this means the events are
077: * generated by the timedElement itself.
078: * @param eventType the type of event this listener listens to. Should
079: * not be null.
080: * @param offset offset from the sync base. This means that time instances
081: * synchronized on the syncBase begin or end time are offset by
082: * this amount.
083: * @throws IllegalArgumentException if eventType is null or if the
084: * <code>ModelNode</code> associated with the
085: * <code>timedElement</code> is null.
086: */
087: public EventBaseCondition(final TimedElementSupport timedElement,
088: final boolean isBegin, final String eventBaseId,
089: final String eventType, final long offset) {
090: this (timedElement, isBegin, eventBaseId,
091: timedElement.animationElement, eventType, offset);
092: }
093:
094: /**
095: * @param timedElement the associated <code>TimedElementSupport</code>.
096: * Should not be null.
097: * @param isBegin defines whether this condition is for a begin list.
098: * @param eventBaseId the id of the element which generates events this
099: * listener listens to. If null, this means the events are
100: * generated by the timedElement itself.
101: * @param eventBase in case eventBaseId is null, this should be used as
102: * the source for generating events. If eventBaseId is null, this
103: * should not be null.
104: * @param eventType the type of event this listener listens to. Should
105: * not be null.
106: * @param offset offset from the sync base. This means that time instances
107: * synchronized on the syncBase begin or end time are offset by
108: * this amount.
109: * @throws IllegalArgumentException if eventType is null or if eventBase
110: * and eventBaseId are null.
111: */
112: protected EventBaseCondition(
113: final TimedElementSupport timedElement,
114: final boolean isBegin, final String eventBaseId,
115: final ModelNode eventBase, final String eventType,
116: final long offset) {
117: super (timedElement, isBegin);
118:
119: if (eventType == null
120: || (eventBaseId == null && eventBase == null)) {
121: throw new IllegalArgumentException();
122: }
123:
124: this .eventBaseId = eventBaseId;
125: this .eventType = eventType;
126: this .offset = offset;
127:
128: ModelNode elt = timedElement.animationElement;
129: if (eventBaseId == null) {
130: setEventBase(eventBase);
131: } else {
132: elt.ownerDocument.resolveIDRef(this , eventBaseId);
133: }
134: }
135:
136: /**
137: * Implementation helper.
138: *
139: * @param eventBase this node's event base.
140: */
141: private void setEventBase(final ModelNode eventBase) {
142: if (this .eventBase != null) {
143: throw new IllegalStateException();
144: }
145:
146: this .eventBase = eventBase;
147:
148: // Register the listener for the bubble phase.
149: eventBase.ownerDocument.getEventSupport().addEventListener(
150: eventBase, eventType, EventSupport.BUBBLE_PHASE, this );
151: }
152:
153: /**
154: * <code>IDRef</code> implementation.
155: *
156: * @param ref the resolved reference (from eventBaseId).
157: */
158: public void resolveTo(final ElementNode ref) {
159: setEventBase(ref);
160: }
161:
162: /**
163: * Implementation of the <code>EventListener</code> interface.
164: * When an event is received from the event base, this condition
165: * should generate a new <code>TimeInstance</code> that will be added
166: * to the associdated <code>TimedElementSupport</code> begin or end instance
167: * list (depending on the <code>isBegin</code> setting). The condition
168: * should also record the time of the new event as its
169: * <code>lastEventTime</code>.
170: *
171: * <p>Note that a condition is sensitive to events according to the
172: * SMIL Animation specification, section 3.6.4 specifying 'Event
173: * Sensitivity' (or SMIL 2 Timing and Synchronization Module,
174: * 'Event Sensitivity').</p>
175: *
176: * @param evt the event that occured
177: */
178: public void handleEvent(final Event evt) {
179: // =====================================================================
180: // If the container is not active, there is no event sensitivity
181: // If the event time is unresolved, there is no event sensitivy either,
182: // as there is no way to compute the time instance that should be
183: // added to the timed element.
184: if (timedElement.timeContainer.state != TimedElementSupport.STATE_PLAYING
185: || !((ModelEvent) evt).eventTime.isResolved()) {
186: return;
187: }
188:
189: // =====================================================================
190: // When the container is active, begin condition are sensitive when:
191: // - there is no current interval yet, i.e., the timedElement is not
192: // active.
193: // - there is a current interval, and the restart behavior is 'always'
194: //
195: // When the container is active (i.e., there is a resolved currentTime),
196: // end condition are sensitive when the element is active and the
197: // restart behavior is never or when active and the restart behavior
198: // is 'always' but there is no begin condition for the event.
199: Time eventTime = new Time(((ModelEvent) evt).eventTime.value);
200: eventTime = timedElement.toContainerSimpleTime(eventTime);
201: if (timedElement.state != TimedElementSupport.STATE_PLAYING) {
202: if (isBegin) {
203: new TimeInstance(timedElement, new Time(eventTime.value
204: + offset), true, isBegin);
205: lastEventTime = eventTime;
206: }
207: } else {
208: // The element is active
209: if (timedElement.restart == TimedElementSupport.RESTART_ALWAYS) {
210: // Only add an instance if this is a begin condition
211: // of if there is not begin event condition
212: if (isBegin || !timedElement.hasBeginCondition(this )) {
213: new TimeInstance(timedElement, new Time(
214: eventTime.value + offset), true, isBegin);
215: lastEventTime = eventTime;
216: }
217: } else {
218: if (!isBegin) {
219: new TimeInstance(timedElement, new Time(
220: eventTime.value + offset), true, isBegin);
221: lastEventTime = eventTime;
222: }
223: }
224: }
225: }
226:
227: /**
228: * Converts this <code>EventBaseCondition</code> to a String trait.
229: *
230: * @return a string describing this <code>TimeCondition</code>
231: */
232: protected String toStringTrait() {
233: StringBuffer sb = new StringBuffer();
234:
235: sb.append(eventBaseId);
236: sb.append('.');
237: sb.append(eventType);
238:
239: if (offset != 0) {
240: if (offset > 0) {
241: sb.append('+');
242: }
243: sb.append(offset / 1000f);
244: sb.append('s');
245: }
246:
247: return sb.toString();
248: }
249:
250: }
|