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 com.sun.perseus.util.SVGConstants;
029:
030: import org.w3c.dom.DOMException;
031:
032: import org.w3c.dom.svg.SVGAnimationElement;
033:
034: import com.sun.perseus.j2d.RenderGraphics;
035:
036: /**
037: * <code>TimedElementNode</code> models <code>ModelNodes</code which
038: * represent Timed Elements.
039: *
040: *
041: * @version $Id: TimedElementNode.java,v 1.6 2006/07/13 00:55:58 st125089 Exp $
042: */
043: public class TimedElementNode extends ElementNode implements
044: SVGAnimationElement {
045: /**
046: * The timing support is added by compositing rather than inheritance
047: * because Java only allows single inheritance.
048: */
049: protected TimedElementSupport timedElementSupport;
050:
051: /**
052: * The timed element's local name.
053: */
054: protected String localName;
055:
056: /**
057: * Builds a new timed element that belongs to the given
058: * document. This <code>TimedElementNode</code> will belong
059: * to the <code>DocumentNode</code>'s time container.
060: *
061: * @param ownerDocument the document this node belongs to.
062: * @param localName the element's local name
063: * @throws IllegalArgumentException if the input ownerDocument is null
064: */
065: public TimedElementNode(final DocumentNode ownerDocument,
066: final String localName) {
067: this (ownerDocument, new TimedElementSupport(), localName);
068: }
069:
070: /**
071: * Builds a new timed element that belongs to the given
072: * document. This <code>TimedElementNode</code> will belong
073: * to the <code>DocumentNode</code>'s time container.
074: *
075: * @param ownerDocument the document this node belongs to.
076: * @throws IllegalArgumentException if the input ownerDocument is null
077: */
078: public TimedElementNode(final DocumentNode ownerDocument) {
079: this (ownerDocument, new TimedElementSupport());
080: }
081:
082: /**
083: * Constructor used by derived classes.
084: *
085: * @param ownerDocument the document this node belongs to.
086: * @param timedElementSupport the associated
087: * <code>TimedElementSupport</code>.
088: * @throws IllegalArgumentException if the input ownerDocument is null.
089: */
090: protected TimedElementNode(final DocumentNode ownerDocument,
091: final TimedElementSupport timedElementSupport) {
092: this (ownerDocument, timedElementSupport,
093: // <!> IMPL NOTE : DO THIS WHILE THE REST OF THE ANIMATION
094: // IMPLEMENTATION IS PENDING.
095: SVGConstants.SVG_SET_TAG);
096: }
097:
098: /**
099: * Constructor used by derived classes.
100: *
101: * @param ownerDocument the document this node belongs to.
102: * @param timedElementSupport the associated
103: * <code>TimedElementSupport</code>.
104: * @param localName the element's local name. Should not be null.
105: * @throws IllegalArgumentException if the input ownerDocument is null
106: * or if the input timedElementSupport is null.
107: */
108: protected TimedElementNode(final DocumentNode ownerDocument,
109: final TimedElementSupport timedElementSupport,
110: final String localName) {
111: super (ownerDocument);
112:
113: if (timedElementSupport == null) {
114: throw new IllegalArgumentException();
115: }
116:
117: if (localName == null) {
118: throw new IllegalArgumentException();
119: }
120:
121: this .timedElementSupport = timedElementSupport;
122:
123: this .localName = localName;
124:
125: timedElementSupport.animationElement = this ;
126: }
127:
128: /**
129: * When a TimedElementNode is hooked into the document tree, it needs
130: * to register with the closest ancestor TimeContainerNode it can
131: * find. If none, it must register with the root time container.
132: */
133: void nodeHookedInDocumentTree() {
134: super .nodeHookedInDocumentTree();
135:
136: ModelNode p = parent;
137: while (p != ownerDocument && p != null) {
138: if (p instanceof TimeContainerNode) {
139: timedElementSupport
140: .setTimeContainer(((TimeContainerNode) p).timeContainerSupport);
141: break;
142: }
143: p = p.parent;
144: }
145:
146: if (p == ownerDocument) {
147: timedElementSupport
148: .setTimeContainer(ownerDocument.timeContainerRootSupport);
149: }
150: }
151:
152: /**
153: * When a TimedElementNode is unhooked from the document tree, it
154: * needs to unregister from its TimeContainer node. Extentions, such
155: * as Animation, may have to perform additional operations, such as
156: * removing themselves from TraitAnim.
157: */
158: void nodeUnhookedFromDocumentTree() {
159: timedElementSupport.setTimeContainer(null);
160: timedElementSupport.reset();
161: }
162:
163: /**
164: * @return the animation tag name passed at construction time.
165: */
166: public String getLocalName() {
167: return localName;
168: }
169:
170: /**
171: * The default value for the begin attribute is '0s'.
172: *
173: * @return an array of trait default values, used if this element
174: * requires that the default trait value be explicitly
175: * set through a setTrait call. This happens, for example,
176: * with the begin trait value on animation elements.
177: */
178: public String[][] getDefaultTraits() {
179: return new String[][] { { SVGConstants.SVG_BEGIN_ATTRIBUTE,
180: "0s" } };
181: }
182:
183: /**
184: * Used by <code>DocumentNode</code> to create a new instance from
185: * a prototype <code>TimedElementNode</code>.
186: *
187: * @param doc the <code>DocumentNode</code> for which a new node is
188: * should be created.
189: * @return a new <code>TimedElementNode</code> for the requested document.
190: */
191: public ElementNode newInstance(final DocumentNode doc) {
192: return new TimedElementNode(doc, new TimedElementSupport(),
193: localName);
194: }
195:
196: /**
197: * @return this node's <code>TimedElementSupport</code>
198: */
199: public TimedElementSupport getTimedElementSupport() {
200: return timedElementSupport;
201: }
202:
203: /**
204: *
205: */
206: public void beginElementAt(float offset) {
207: timedElementSupport.beginAt((long) (offset * 1000));
208: }
209:
210: /**
211: * Creates a begin instance time for the current time.
212: */
213: public void beginElement() {
214: timedElementSupport.beginAt(0);
215: }
216:
217: /**
218: *
219: */
220: public void endElementAt(float offset) {
221: timedElementSupport.endAt((long) (offset * 1000));
222: }
223:
224: /**
225: * Creates an end instance time for the current time.
226: */
227: public void endElement() {
228: timedElementSupport.endAt(0);
229: }
230:
231: /**
232: * Pauses the element. If the element is already paused, this method has no
233: * effect. See the SMIL 2 specification for a description of <a
234: * href="http://www.w3.org/TR/2001/REC-smil20-20010807/smil20.html#smil-timing-Timing-PausedElementsAndActiveDur">pausing
235: * elements</a>.
236: */
237: public void pauseElement() {
238: throw new Error("NOT IMPLEMENTED");
239: }
240:
241: /**
242: * Unpauses the element if it was paused. If the element was not paused,
243: * this method has no effect. See the SMIL 2 specification for a description
244: * of <a
245: * href="http://www.w3.org/TR/2001/REC-smil20-20010807/smil20.html#smil-timing-Timing-PausedElementsAndActiveDur">pausing
246: * elements</a>.
247: */
248: public void unpauseElement() {
249: throw new Error("NOT IMPLEMENTED");
250: }
251:
252: /**
253: * @return true if the element is currently paused, false otherwise. See the
254: * SMIL 2 specification for a description of <a
255: * href="http://www.w3.org/TR/2001/REC-smil20-20010807/smil20.html#smil-timing-Timing-PausedElementsAndActiveDur">pausing
256: * elements</a>.
257: */
258: public boolean getElementPaused() {
259: throw new Error("NOT IMPLEMENTED");
260: }
261:
262: /**
263: * TimedElementNode supports the begin, end, dur, min, max, restart,
264: * repeatCount, repeatDur and fill attributes.
265: *
266: * @param traitName the name of the trait which the element may support.
267: * @return true if this element supports the given trait in one of the
268: * trait accessor methods (such as <code>getTrait</code> or
269: * <code>setFloatTrait</code>.
270: */
271: boolean supportsTrait(final String traitName) {
272: if (SVGConstants.SVG_BEGIN_ATTRIBUTE == traitName
273: || SVGConstants.SVG_END_ATTRIBUTE == traitName
274: || SVGConstants.SVG_DUR_ATTRIBUTE == traitName
275: || SVGConstants.SVG_MIN_ATTRIBUTE == traitName
276: || SVGConstants.SVG_MAX_ATTRIBUTE == traitName
277: || SVGConstants.SVG_RESTART_ATTRIBUTE == traitName
278: || SVGConstants.SVG_REPEAT_COUNT_ATTRIBUTE == traitName
279: || SVGConstants.SVG_REPEAT_DUR_ATTRIBUTE == traitName
280: || SVGConstants.SVG_FILL_ATTRIBUTE == traitName) {
281: return true;
282: }
283:
284: return super .supportsTrait(traitName);
285: }
286:
287: // JAVADOC COMMENT ELIDED
288: public String getTraitImpl(final String name) throws DOMException {
289: if (SVGConstants.SVG_BEGIN_ATTRIBUTE == name) {
290: if (timedElementSupport.beginConditions.size() == 0) {
291: return "0s";
292: }
293: return TimeCondition
294: .toStringTrait(timedElementSupport.beginConditions);
295: } else if (SVGConstants.SVG_END_ATTRIBUTE == name) {
296: if (timedElementSupport.endConditions.size() == 0) {
297: return SVGConstants.SVG_INDEFINITE_VALUE;
298: }
299: return TimeCondition
300: .toStringTrait(timedElementSupport.endConditions);
301: } else if (SVGConstants.SVG_DUR_ATTRIBUTE == name) {
302: return Time.toStringTrait(timedElementSupport.dur);
303: } else if (SVGConstants.SVG_MIN_ATTRIBUTE == name) {
304: return Time.toStringTrait(timedElementSupport.min);
305: } else if (SVGConstants.SVG_MAX_ATTRIBUTE == name) {
306: return Time.toStringTrait(timedElementSupport.max);
307: } else if (SVGConstants.SVG_RESTART_ATTRIBUTE == name) {
308: switch (timedElementSupport.restart) {
309: case TimedElementSupport.RESTART_ALWAYS:
310: return SVGConstants.SVG_ALWAYS_VALUE;
311: case TimedElementSupport.RESTART_WHEN_NOT_ACTIVE:
312: return SVGConstants.SVG_WHEN_NOT_ACTIVE_VALUE;
313: case TimedElementSupport.RESTART_NEVER:
314: return SVGConstants.SVG_NEVER_VALUE;
315: default:
316: throw new IllegalStateException();
317: }
318: } else if (SVGConstants.SVG_REPEAT_COUNT_ATTRIBUTE == name) {
319: if (Float.isNaN(timedElementSupport.repeatCount)) {
320: return null; // Unspecified
321: } else if (timedElementSupport.repeatCount == Float.MAX_VALUE) {
322: return SVGConstants.SVG_INDEFINITE_VALUE;
323: }
324: return Float.toString(timedElementSupport.repeatCount);
325: } else if (SVGConstants.SVG_REPEAT_DUR_ATTRIBUTE == name) {
326: return Time.toStringTrait(timedElementSupport.repeatDur);
327: } else if (SVGConstants.SVG_FILL_ATTRIBUTE == name) {
328: switch (timedElementSupport.fillBehavior) {
329: case TimedElementSupport.FILL_BEHAVIOR_REMOVE:
330: return SVGConstants.SVG_REMOVE_VALUE;
331: case TimedElementSupport.FILL_BEHAVIOR_FREEZE:
332: return SVGConstants.SVG_FREEZE_VALUE;
333: default:
334: throw new IllegalStateException();
335: }
336: } else {
337: return super .getTraitImpl(name);
338: }
339: }
340:
341: /**
342: * Parses the input value an creates TimeCondition instances for the
343: * current instance.
344: *
345: * @param traitName the name of the time condition trait.
346: * @param value the trait value.
347: * @param isBegin true if this should be parsed as a begin value, i.e.,
348: * with a 0s default.
349: * @throws DOMException if the input value is invalid.
350: */
351: protected void parseTimeConditionsTrait(final String traitName,
352: final String value, final boolean isBegin)
353: throws DOMException {
354: try {
355: ownerDocument.timeConditionParser.parseBeginEndAttribute(
356: value, this , isBegin);
357: } catch (IllegalArgumentException iae) {
358: iae.printStackTrace();
359: throw illegalTraitValue(traitName, value);
360: }
361: }
362:
363: // JAVADOC COMMENT ELIDED
364: public void setTraitImpl(final String name, final String value)
365: throws DOMException {
366: if (SVGConstants.SVG_BEGIN_ATTRIBUTE == name) {
367: checkWriteLoading(name);
368: timedElementSupport.beginConditions.removeAllElements();
369: parseTimeConditionsTrait(name, value, true);
370: } else if (SVGConstants.SVG_END_ATTRIBUTE == name) {
371: checkWriteLoading(name);
372: timedElementSupport.endConditions.removeAllElements();
373: parseTimeConditionsTrait(name, value, false);
374: } else if (SVGConstants.SVG_DUR_ATTRIBUTE == name) {
375: checkWriteLoading(name);
376: // Ignore 'media'
377: if (SVGConstants.SVG_MEDIA_VALUE.equals(value)) {
378: return;
379: }
380: timedElementSupport.setDur(parseClockTrait(name, value));
381: } else if (SVGConstants.SVG_MIN_ATTRIBUTE == name) {
382: checkWriteLoading(name);
383:
384: // Ignore 'media'
385: if (SVGConstants.SVG_MEDIA_VALUE.equals(value)) {
386: return;
387: }
388: timedElementSupport.setMin(parseMinMaxClock(name, value,
389: true));
390: } else if (SVGConstants.SVG_MAX_ATTRIBUTE == name) {
391: checkWriteLoading(name);
392:
393: // Ignore 'media'
394: if (SVGConstants.SVG_MEDIA_VALUE.equals(value)) {
395: return;
396: }
397: timedElementSupport.setMax(parseMinMaxClock(name, value,
398: false));
399: } else if (SVGConstants.SVG_RESTART_ATTRIBUTE == name) {
400: checkWriteLoading(name);
401:
402: if (SVGConstants.SVG_ALWAYS_VALUE.equals(value)) {
403: timedElementSupport.restart = TimedElementSupport.RESTART_ALWAYS;
404: } else if (SVGConstants.SVG_WHEN_NOT_ACTIVE_VALUE
405: .equals(value)) {
406: timedElementSupport.restart = TimedElementSupport.RESTART_WHEN_NOT_ACTIVE;
407: } else if (SVGConstants.SVG_NEVER_VALUE.equals(value)) {
408: timedElementSupport.restart = TimedElementSupport.RESTART_NEVER;
409: } else {
410: throw illegalTraitValue(name, value);
411: }
412: } else if (SVGConstants.SVG_REPEAT_COUNT_ATTRIBUTE == name) {
413: checkWriteLoading(name);
414:
415: if (SVGConstants.SVG_INDEFINITE_VALUE.equals(value)) {
416: timedElementSupport.repeatCount = Float.MAX_VALUE;
417: } else {
418: timedElementSupport.repeatCount = parseFloatTrait(name,
419: value);
420: }
421: } else if (SVGConstants.SVG_REPEAT_DUR_ATTRIBUTE == name) {
422: checkWriteLoading(name);
423: if (SVGConstants.SVG_INDEFINITE_VALUE.equals(value)) {
424: timedElementSupport.repeatDur = Time.INDEFINITE;
425: } else {
426: timedElementSupport.setRepeatDur(parseClockTrait(name,
427: value));
428: }
429: } else if (SVGConstants.SVG_FILL_ATTRIBUTE == name) {
430: checkWriteLoading(name);
431: if (SVGConstants.SVG_REMOVE_VALUE.equals(value)) {
432: timedElementSupport.fillBehavior = TimedElementSupport.FILL_BEHAVIOR_REMOVE;
433: } else if (SVGConstants.SVG_FREEZE_VALUE.equals(value)) {
434: timedElementSupport.fillBehavior = TimedElementSupport.FILL_BEHAVIOR_FREEZE;
435: } else {
436: throw illegalTraitValue(name, value);
437: }
438: } else {
439: super.setTraitImpl(name, value);
440: }
441: }
442:
443: }
|