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:
027: package com.sun.perseus.model;
028:
029: import org.w3c.dom.DOMException;
030:
031: /**
032: * The <code>TraitAnim</code> class is the link between animation targets
033: * (i.e., traits on elements) and animation elements.
034: *
035: * <p>When an animation becomes active on a target (i.e., on a trait or pseudo
036: * trait for an element), it invokes getAnimTrait(traitName) on the
037: * corresponding element to get the trait's TraitAnim. One is created if the
038: * trait is not currently animated. Then, the animation adds itself to the
039: * TraitAnim through a call to addAnimation(). As an animation adds itself, it
040: * becomes the TraitAnim's rootAnim. If there was no animation, the new
041: * animation's baseVal becomes the TraitAnim itself. If there was already an
042: * animation, the new Animation's baseVal becomes the previous rootAnim.</p>
043: *
044: * <p>When an animation becomes inactive, it removes itself from the TraitAnim
045: * by calling the removeAnimation method. When the last active animation removes
046: * itself from the TraitAnim, the TraitAnim removes itself from the
047: * ElementNode's TraitAnim list and it restores the trait's original base value
048: * (stored as baseValue).</p>
049: *
050: * <p>This achieves the sandwich model behavior described in the SMIL Animation
051: * specification (section 3.5). In particular, because the tree is sampled in
052: * document order in Perseus, the animation that appears first in document order
053: * will have lower priority (i.e., it will be added first to the TraitAnim). If
054: * an animation is a time dependent of another one, then it will become active
055: * after its time sync and will have higher priority than its time sync.</p>
056: *
057: * @version $Id: TraitAnim.java,v 1.4 2006/06/29 10:47:36 ln156897 Exp $
058: */
059: abstract class TraitAnim implements BaseValue {
060: /**
061: * The TraitAnim underlying type. One of ElementNode.TRAIT_TYPE
062: * constants.
063: */
064: String traitType;
065:
066: /**
067: * This animation's root.
068: */
069: Animation rootAnim;
070:
071: /**
072: * The target element.
073: */
074: ElementNode targetElement;
075:
076: /**
077: * The target trait name.
078: */
079: String traitName;
080:
081: /**
082: * The target trait namespace.
083: */
084: String traitNamespace;
085:
086: /**
087: * The trait's specified value, as a String.
088: */
089: String specifiedTraitValue;
090:
091: /**
092: * True when the TraitAnim has at least one active animation.
093: */
094: boolean active;
095:
096: /**
097: * Constructs a new TraitAnim for a given ElementNode trait
098: * in the given namespace.
099: *
100: * @param targetElement the ElementNode whose trait is animated.
101: * Should not be null.
102: * @param targetNamespace the target trait's namespace. Should not
103: * be null. The per-element partition namespace should be
104: * represented by the ElementNode.NULL_NS value.
105: * @param targetTrait the name of the animated trait. Should not
106: * be null.
107: */
108: TraitAnim(final ElementNode targetElement,
109: final String traitNamespace, final String traitName,
110: final String traitType) {
111: if (targetElement == null || traitName == null
112: || traitNamespace == null || traitType == null) {
113: throw new NullPointerException();
114: }
115:
116: this .targetElement = targetElement;
117: this .traitNamespace = traitNamespace;
118: this .traitName = traitName;
119: this .traitType = traitType;
120: }
121:
122: /**
123: * @return the trait's specified base value, as a String.
124: */
125: public String getSpecifiedTraitNS() {
126: if (specifiedTraitValue == null) {
127: specifiedTraitValue = targetElement
128: .getSpecifiedTraitNSImpl(traitNamespace, traitName);
129: }
130:
131: return specifiedTraitValue;
132: }
133:
134: /**
135: * Restores the base value. This is invoked when there are not more
136: * animations and the original base value needs to be restored.
137: */
138: final void restore() {
139: // Now, restore the specified trait value
140: if (traitNamespace == ElementNode.NULL_NS) {
141: targetElement.setTraitImpl(traitName, specifiedTraitValue);
142: } else {
143: targetElement.setTraitNSImpl(traitNamespace, traitName,
144: specifiedTraitValue);
145: }
146: }
147:
148: /**
149: * Adds a new animation to this TraitAnim. The new animation
150: * becomes the highest priority animation. If this is the
151: * first animation added to the TraitAnim, the new animation's
152: * base value becomes the TraitAnim itself and the TraitAnim
153: * registers with the DocumentNode. If there is already
154: * one or more animation in the TraitAnim, the baseValue for the
155: * new animation becomes the previous animation root. In all
156: * cases, the new animation becomes the rootAnim.
157: *
158: * @param newAnim the new highest priority animation for this TraitAnim.
159: * Should not be null.
160: */
161: void addAnimation(final Animation newAnim) {
162: // Reject null values
163: if (newAnim == null) {
164: throw new NullPointerException();
165: }
166:
167: if (rootAnim == null) {
168: // This is the first animation.
169: // Set the animation as the root animation and set its
170: // baseValue.
171: rootAnim = newAnim;
172: newAnim.baseVal = this ;
173: targetElement.ownerDocument.activeTraitAnims
174: .addElement(this );
175:
176: // We need to recompute the specifiedTraitAnim at this point
177: // Otherwise, the specifiedTrait value may be off (i.e., different
178: // from what it was set to originally, when the TraitAnim was
179: // created.
180: this .specifiedTraitValue = targetElement
181: .getSpecifiedTraitNSImpl(traitNamespace, traitName);
182: } else {
183: // This is a new animation in the sandwich.
184: // The new animation becomes the highest priority animation and
185: // its baseValue is the previous rootAnim.
186: newAnim.baseVal = rootAnim;
187: rootAnim = newAnim;
188: }
189:
190: active = true;
191: }
192:
193: /**
194: * Removes the input animation from this TraitAnim. If the removed
195: * animation's baseValue is the TraitAnim itself, it means this is
196: * the last active animation on the trait and the TraitAnim will
197: * mark itself as inactive. If this is not the last animation,
198: * then this animation's baseValue becomes the rootAnim.
199: *
200: * If removedAnim is not part of this TraitAnim, this method
201: * does nothing.
202: *
203: * @param removedAnim the animation to remove from the TraitAnim.
204: * should not be null.
205: */
206: void removeAnimation(final Animation removedAnim) {
207: // Reject null values.
208: if (removedAnim == null) {
209: throw new NullPointerException();
210: }
211:
212: if (removedAnim == rootAnim) {
213: // Removing the root animatoin
214: if (removedAnim.baseVal == this ) {
215: // This is the last animation in the TraitAnim.
216: // Unregister from the Document.
217: targetElement.ownerDocument.activeTraitAnims
218: .removeElement(this );
219: rootAnim = null;
220:
221: // Mark the animation as inactive.
222: active = false;
223: restore();
224: } else {
225: rootAnim = (Animation) removedAnim.baseVal;
226: }
227: } else {
228: if (rootAnim != null) {
229: // Removing an animation other than the root.
230: // Find the preceding animation.
231: Animation prevAnim = null;
232: Animation curAnim = rootAnim;
233: while (curAnim.baseVal != this ) {
234: if (curAnim.baseVal == removedAnim) {
235: prevAnim = curAnim;
236: break;
237: }
238: curAnim = (Animation) curAnim.baseVal;
239: }
240:
241: // If removedAnimat was indeed part of the sandwich.
242: if (prevAnim != null) {
243: prevAnim.baseVal = removedAnim.baseVal;
244: }
245: }
246: }
247: }
248:
249: /**
250: * @param traitType the expected type for this trait. One of the
251: * ElementNode.TRAIT_TYPE_.... constants (e.g., TRAIT_TYPE_STRING).
252: * All TraitAnim implementations must support TRAIT_TYPE_STRING.
253: * They may support additional types (for example, FloatTraitAnim
254: * may support TRAIT_TYPE_FLOAT).
255: * @return the trait's computed value, as a String.
256: *
257: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
258: * trait's value cannot be specified as a requested type value.
259: */
260: final String getTrait(final String traitType) throws DOMException {
261: if (ElementNode.TRAIT_TYPE_STRING.equals(traitType)
262: || this .traitType.equals(traitType)) {
263: return getTraitImpl();
264: } else {
265: throw targetElement.unsupportedTraitTypeNS(traitName,
266: traitNamespace, traitType);
267: }
268: }
269:
270: /**
271: * @return the trait's value, as a String.
272: */
273: abstract protected String getTraitImpl();
274:
275: /**
276: * Sets the trait's base value, as a String.
277: *
278: * @param value the new trait base value.
279: * @param traitType the requested trait type.
280: *
281: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
282: * trait's value cannot be specified as the requested traitType.
283: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
284: * value is an invalid value for the given trait or null.
285: */
286: final void setTrait(String value, String traitType)
287: throws DOMException {
288: if (ElementNode.TRAIT_TYPE_STRING.equals(traitType)
289: || this .traitType.equals(traitType)) {
290: setTraitImpl(value);
291: } else {
292: throw targetElement.unsupportedTraitTypeNS(traitName,
293: traitNamespace, traitType);
294: }
295: }
296:
297: /**
298: * Sets the trait's base value, as a String.
299: *
300: * @param value the new trait base value.
301: *
302: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
303: * value is an invalid value for the given trait or null.
304: */
305: abstract void setTraitImpl(String value) throws DOMException;
306:
307: /**
308: * Applies the animation effect. The implementation makes sure it
309: * implements the sandwich model by 'pulling' values from the
310: * root animation (i.e., the animation with the highest priority).
311: */
312: abstract void apply();
313:
314: /**
315: * Converts the input values set to a RefValues object.
316: *
317: * @param anim the <code>Animation</code> for which the values should be
318: * converted.
319: * @param values a semi-colon seperated list of values which need to be
320: * validated.
321: * @param reqTraitNamespace the namespace of the trait which has the values
322: * value on the requesting element.
323: * @param reqTraitName the name of the trait which has the values value on
324: * the requesting element.
325: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
326: * value is incompatible with the given trait.
327: */
328: abstract RefValues toRefValues(final Animation anim,
329: final String[] values, final String reqTraitNamespace,
330: final String reqTraitName) throws DOMException;
331:
332: /**
333: * Used to sum two animated trait values.
334: *
335: * @param valueA the base value. May be null.
336: * @param valueB the value to add to the base value. If the baseValue
337: * @return the sum result.
338: */
339: abstract Object[] sum(Object[] valueA, Object[] valueB);
340:
341: /**
342: * Used to multiply an animated trait value by a number of iterations.
343: *
344: * @param value the animated trait value to multiply.
345: * @param iter the number of iteration to account for.
346: * @return the multiply result.
347: */
348: abstract Object[] multiply(Object[] value, int iter);
349:
350: /**
351: * @return true if this trait supports interpolation. false otherwise.
352: */
353: boolean supportsInterpolation() {
354: return false;
355: }
356: }
|