0001: /*
0002:
0003: Licensed to the Apache Software Foundation (ASF) under one or more
0004: contributor license agreements. See the NOTICE file distributed with
0005: this work for additional information regarding copyright ownership.
0006: The ASF licenses this file to You under the Apache License, Version 2.0
0007: (the "License"); you may not use this file except in compliance with
0008: the License. You may obtain a copy of the License at
0009:
0010: http://www.apache.org/licenses/LICENSE-2.0
0011:
0012: Unless required by applicable law or agreed to in writing, software
0013: distributed under the License is distributed on an "AS IS" BASIS,
0014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: See the License for the specific language governing permissions and
0016: limitations under the License.
0017:
0018: */
0019: package org.apache.batik.bridge;
0020:
0021: import java.awt.Color;
0022: import java.awt.Paint;
0023: import java.lang.ref.WeakReference;
0024: import java.util.Calendar;
0025: import java.util.Date;
0026: import java.util.HashSet;
0027: import java.util.LinkedList;
0028: import java.util.Arrays;
0029: import java.util.Set;
0030:
0031: import org.apache.batik.anim.AnimationEngine;
0032: import org.apache.batik.anim.AnimationException;
0033: import org.apache.batik.dom.anim.AnimationTarget;
0034: import org.apache.batik.anim.timing.TimedDocumentRoot;
0035: import org.apache.batik.anim.timing.TimedElement;
0036: import org.apache.batik.anim.values.AnimatableAngleValue;
0037: import org.apache.batik.anim.values.AnimatableAngleOrIdentValue;
0038: import org.apache.batik.anim.values.AnimatableBooleanValue;
0039: import org.apache.batik.anim.values.AnimatableIntegerValue;
0040: import org.apache.batik.anim.values.AnimatableLengthValue;
0041: import org.apache.batik.anim.values.AnimatableLengthListValue;
0042: import org.apache.batik.anim.values.AnimatableLengthOrIdentValue;
0043: import org.apache.batik.anim.values.AnimatableNumberValue;
0044: import org.apache.batik.anim.values.AnimatableNumberListValue;
0045: import org.apache.batik.anim.values.AnimatableNumberOrPercentageValue;
0046: import org.apache.batik.anim.values.AnimatablePathDataValue;
0047: import org.apache.batik.anim.values.AnimatablePointListValue;
0048: import org.apache.batik.anim.values.AnimatablePreserveAspectRatioValue;
0049: import org.apache.batik.anim.values.AnimatableNumberOrIdentValue;
0050: import org.apache.batik.anim.values.AnimatableStringValue;
0051: import org.apache.batik.anim.values.AnimatableValue;
0052: import org.apache.batik.anim.values.AnimatableColorValue;
0053: import org.apache.batik.anim.values.AnimatablePaintValue;
0054: import org.apache.batik.css.engine.CSSEngine;
0055: import org.apache.batik.css.engine.CSSStylableElement;
0056: import org.apache.batik.css.engine.StyleMap;
0057: import org.apache.batik.css.engine.value.FloatValue;
0058: import org.apache.batik.css.engine.value.StringValue;
0059: import org.apache.batik.css.engine.value.Value;
0060: import org.apache.batik.css.engine.value.ValueManager;
0061: import org.apache.batik.dom.svg.SVGOMDocument;
0062: import org.apache.batik.dom.svg.SVGOMElement;
0063: import org.apache.batik.dom.svg.SVGStylableElement;
0064: import org.apache.batik.parser.DefaultPreserveAspectRatioHandler;
0065: import org.apache.batik.parser.FloatArrayProducer;
0066: import org.apache.batik.parser.DefaultLengthHandler;
0067: import org.apache.batik.parser.LengthArrayProducer;
0068: import org.apache.batik.parser.LengthHandler;
0069: import org.apache.batik.parser.LengthListParser;
0070: import org.apache.batik.parser.LengthParser;
0071: import org.apache.batik.parser.NumberListParser;
0072: import org.apache.batik.parser.PathArrayProducer;
0073: import org.apache.batik.parser.PathParser;
0074: import org.apache.batik.parser.PointsParser;
0075: import org.apache.batik.parser.ParseException;
0076: import org.apache.batik.parser.PreserveAspectRatioHandler;
0077: import org.apache.batik.parser.PreserveAspectRatioParser;
0078: import org.apache.batik.util.RunnableQueue;
0079: import org.apache.batik.util.SMILConstants;
0080: import org.apache.batik.util.XMLConstants;
0081:
0082: import org.w3c.dom.Document;
0083: import org.w3c.dom.Element;
0084: import org.w3c.dom.css.CSSPrimitiveValue;
0085: import org.w3c.dom.css.CSSStyleDeclaration;
0086: import org.w3c.dom.css.CSSValue;
0087: import org.w3c.dom.events.EventTarget;
0088: import org.w3c.dom.svg.SVGAngle;
0089: import org.w3c.dom.svg.SVGLength;
0090: import org.w3c.dom.svg.SVGPreserveAspectRatio;
0091:
0092: /**
0093: * An AnimationEngine for SVG documents.
0094: *
0095: * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
0096: * @version $Id: SVGAnimationEngine.java 522170 2007-03-25 07:18:46Z cam $
0097: */
0098: public class SVGAnimationEngine extends AnimationEngine {
0099:
0100: /**
0101: * The BridgeContext to use for value parsing.
0102: */
0103: protected BridgeContext ctx;
0104:
0105: /**
0106: * The CSSEngine used for CSS value parsing.
0107: */
0108: protected CSSEngine cssEngine;
0109:
0110: /**
0111: * Whether animation processing has started. This affects whether
0112: * animation element bridges add their animation on to the initial
0113: * bridge list, or process them immediately.
0114: */
0115: protected boolean started;
0116:
0117: /**
0118: * The Runnable that ticks the document.
0119: */
0120: protected AnimationTickRunnable animationTickRunnable;
0121:
0122: /**
0123: * The factory for unparsed string values.
0124: */
0125: protected UncomputedAnimatableStringValueFactory uncomputedAnimatableStringValueFactory = new UncomputedAnimatableStringValueFactory();
0126:
0127: /**
0128: * The factory for length-or-ident values.
0129: */
0130: protected AnimatableLengthOrIdentFactory animatableLengthOrIdentFactory = new AnimatableLengthOrIdentFactory();
0131:
0132: /**
0133: * The factory for number-or-ident values.
0134: */
0135: protected AnimatableNumberOrIdentFactory animatableNumberOrIdentFactory = new AnimatableNumberOrIdentFactory(
0136: false);
0137:
0138: /**
0139: * Factories for {@link AnimatableValue} parsing.
0140: */
0141: protected Factory[] factories = { null, // TYPE_UNKNOWN
0142: new AnimatableIntegerValueFactory(), // TYPE_INTEGER
0143: new AnimatableNumberValueFactory(), // TYPE_NUMBER
0144: new AnimatableLengthValueFactory(), // TYPE_LENGTH
0145: null, // TYPE_NUMBER_OPTIONAL_NUMBER
0146: new AnimatableAngleValueFactory(), // TYPE_ANGLE
0147: new AnimatableColorValueFactory(), // TYPE_COLOR
0148: new AnimatablePaintValueFactory(), // TYPE_PAINT
0149: null, // TYPE_PERCENTAGE
0150: null, // TYPE_TRANSFORM_LIST
0151: uncomputedAnimatableStringValueFactory, // TYPE_URI
0152: null, // TYPE_FREQUENCY
0153: null, // TYPE_TIME
0154: new AnimatableNumberListValueFactory(), // TYPE_NUMBER_LIST
0155: new AnimatableLengthListValueFactory(), // TYPE_LENGTH_LIST
0156: uncomputedAnimatableStringValueFactory, // TYPE_IDENT
0157: uncomputedAnimatableStringValueFactory, // TYPE_CDATA
0158: animatableLengthOrIdentFactory, // TYPE_LENGTH_OR_INHERIT
0159: uncomputedAnimatableStringValueFactory, // TYPE_IDENT_LIST
0160: uncomputedAnimatableStringValueFactory, // TYPE_CLIP_VALUE
0161: uncomputedAnimatableStringValueFactory, // TYPE_URI_OR_IDENT
0162: uncomputedAnimatableStringValueFactory, // TYPE_CURSOR_VALUE
0163: new AnimatablePathDataFactory(), // TYPE_PATH_DATA
0164: uncomputedAnimatableStringValueFactory, // TYPE_ENABLE_BACKGROUND_VALUE
0165: null, // TYPE_TIME_VALUE_LIST
0166: animatableNumberOrIdentFactory, // TYPE_NUMBER_OR_INHERIT
0167: uncomputedAnimatableStringValueFactory, // TYPE_FONT_FAMILY_VALUE
0168: null, // TYPE_FONT_FACE_FONT_SIZE_VALUE
0169: new AnimatableNumberOrIdentFactory(true), // TYPE_FONT_WEIGHT_VALUE
0170: new AnimatableAngleOrIdentFactory(), // TYPE_ANGLE_OR_IDENT
0171: null, // TYPE_KEY_SPLINES_VALUE
0172: new AnimatablePointListValueFactory(), // TYPE_POINTS_VALUE
0173: new AnimatablePreserveAspectRatioValueFactory(), // TYPE_PRESERVE_ASPECT_RATIO_VALUE
0174: null, // TYPE_URI_LIST
0175: uncomputedAnimatableStringValueFactory, // TYPE_LENGTH_LIST_OR_IDENT
0176: null, // TYPE_CHARACTER_OR_UNICODE_RANGE_LIST
0177: null, // TYPE_UNICODE_RANGE_LIST
0178: null, // TYPE_FONT_VALUE
0179: null, // TYPE_FONT_DECSRIPTOR_SRC_VALUE
0180: animatableLengthOrIdentFactory, // TYPE_FONT_SIZE_VALUE
0181: animatableLengthOrIdentFactory, // TYPE_BASELINE_SHIFT_VALUE
0182: animatableLengthOrIdentFactory, // TYPE_KERNING_VALUE
0183: animatableLengthOrIdentFactory, // TYPE_SPACING_VALUE
0184: animatableLengthOrIdentFactory, // TYPE_LINE_HEIGHT_VALUE
0185: animatableNumberOrIdentFactory, // TYPE_FONT_SIZE_ADJUST_VALUE
0186: null, // TYPE_LANG_VALUE
0187: null, // TYPE_LANG_LIST_VALUE
0188: new AnimatableNumberOrPercentageValueFactory(), // TYPE_NUMBER_OR_PERCENTAGE
0189: null, // TYPE_TIMING_SPECIFIER_LIST
0190: new AnimatableBooleanValueFactory(), // TYPE_BOOLEAN
0191: };
0192:
0193: /**
0194: * Whether the document is an SVG 1.2 document.
0195: */
0196: protected boolean isSVG12;
0197:
0198: /**
0199: * List of bridges that will be initialized when the document is started.
0200: */
0201: protected LinkedList initialBridges = new LinkedList();
0202:
0203: /**
0204: * A StyleMap used by the {@link Factory}s when computing CSS values.
0205: */
0206: protected StyleMap dummyStyleMap;
0207:
0208: /**
0209: * The thread that ticks the animation engine.
0210: */
0211: protected AnimationThread animationThread;
0212:
0213: /**
0214: * The animation limiting mode.
0215: */
0216: protected int animationLimitingMode;
0217:
0218: /**
0219: * The amount of animation limiting.
0220: */
0221: protected float animationLimitingAmount;
0222:
0223: /**
0224: * Set of SMIL animation event names for SVG 1.1.
0225: */
0226: protected static final Set animationEventNames11 = new HashSet();
0227:
0228: /**
0229: * Set of SMIL animation event names for SVG 1.2.
0230: */
0231: protected static final Set animationEventNames12 = new HashSet();
0232:
0233: static {
0234: String[] eventNamesCommon = { "click", "mousedown", "mouseup",
0235: "mouseover", "mousemove", "mouseout", "beginEvent",
0236: "endEvent" };
0237: String[] eventNamesSVG11 = { "DOMSubtreeModified",
0238: "DOMNodeInserted", "DOMNodeRemoved",
0239: "DOMNodeRemovedFromDocument",
0240: "DOMNodeInsertedIntoDocument", "DOMAttrModified",
0241: "DOMCharacterDataModified", "SVGLoad", "SVGUnload",
0242: "SVGAbort", "SVGError", "SVGResize", "SVGScroll",
0243: "repeatEvent" };
0244: String[] eventNamesSVG12 = { "load", "resize", "scroll", "zoom" };
0245: for (int i = 0; i < eventNamesCommon.length; i++) {
0246: animationEventNames11.add(eventNamesCommon[i]);
0247: animationEventNames12.add(eventNamesCommon[i]);
0248: }
0249: for (int i = 0; i < eventNamesSVG11.length; i++) {
0250: animationEventNames11.add(eventNamesSVG11[i]);
0251: }
0252: for (int i = 0; i < eventNamesSVG12.length; i++) {
0253: animationEventNames12.add(eventNamesSVG12[i]);
0254: }
0255: }
0256:
0257: /**
0258: * Creates a new SVGAnimationEngine.
0259: */
0260: public SVGAnimationEngine(Document doc, BridgeContext ctx) {
0261: super (doc);
0262: this .ctx = ctx;
0263: SVGOMDocument d = (SVGOMDocument) doc;
0264: cssEngine = d.getCSSEngine();
0265: dummyStyleMap = new StyleMap(cssEngine.getNumberOfProperties());
0266: isSVG12 = d.isSVG12();
0267: }
0268:
0269: /**
0270: * Disposes this animation engine.
0271: */
0272: public void dispose() {
0273: synchronized (this ) {
0274: pause();
0275: super .dispose();
0276: }
0277: }
0278:
0279: /**
0280: * Adds an animation element bridge to the list of bridges that
0281: * require initializing when the document is started.
0282: */
0283: public void addInitialBridge(SVGAnimationElementBridge b) {
0284: if (initialBridges != null) {
0285: initialBridges.add(b);
0286: }
0287: }
0288:
0289: /**
0290: * Returns whether animation processing has begun.
0291: */
0292: public boolean hasStarted() {
0293: return started;
0294: }
0295:
0296: /**
0297: * Parses an AnimatableValue.
0298: */
0299: public AnimatableValue parseAnimatableValue(Element animElt,
0300: AnimationTarget target, String ns, String ln,
0301: boolean isCSS, String s) {
0302: SVGOMElement elt = (SVGOMElement) target.getElement();
0303: int type;
0304: if (isCSS) {
0305: type = elt.getPropertyType(ln);
0306: } else {
0307: type = elt.getAttributeType(ns, ln);
0308: }
0309: Factory factory = factories[type];
0310: if (factory == null) {
0311: String an = ns == null ? ln : '{' + ns + '}' + ln;
0312: throw new BridgeException(ctx, animElt,
0313: "attribute.not.animatable", new Object[] {
0314: target.getElement().getNodeName(), an });
0315: }
0316: return factories[type].createValue(target, ns, ln, isCSS, s);
0317: }
0318:
0319: /**
0320: * Returns an AnimatableValue for the underlying value of a CSS property.
0321: */
0322: public AnimatableValue getUnderlyingCSSValue(Element animElt,
0323: AnimationTarget target, String pn) {
0324: ValueManager[] vms = cssEngine.getValueManagers();
0325: int idx = cssEngine.getPropertyIndex(pn);
0326: if (idx != -1) {
0327: int type = vms[idx].getPropertyType();
0328: Factory factory = factories[type];
0329: if (factory == null) {
0330: throw new BridgeException(ctx, animElt,
0331: "attribute.not.animatable", new Object[] {
0332: target.getElement().getNodeName(), pn });
0333: }
0334: SVGStylableElement e = (SVGStylableElement) target
0335: .getElement();
0336: CSSStyleDeclaration over = e.getOverrideStyle();
0337: String oldValue = over.getPropertyValue(pn);
0338: if (oldValue != null) {
0339: over.removeProperty(pn);
0340: }
0341: Value v = cssEngine.getComputedStyle(e, null, idx);
0342: if (oldValue != null && !oldValue.equals("")) {
0343: over.setProperty(pn, oldValue, null);
0344: }
0345: return factories[type].createValue(target, pn, v);
0346: }
0347: // XXX Doesn't handle shorthands.
0348: return null;
0349: }
0350:
0351: /**
0352: * Pauses the animations.
0353: */
0354: public void pause() {
0355: super .pause();
0356: UpdateManager um = ctx.getUpdateManager();
0357: if (um != null) {
0358: um.getUpdateRunnableQueue().setIdleRunnable(null);
0359: }
0360: }
0361:
0362: /**
0363: * Pauses the animations.
0364: */
0365: public void unpause() {
0366: super .unpause();
0367: UpdateManager um = ctx.getUpdateManager();
0368: if (um != null) {
0369: um.getUpdateRunnableQueue().setIdleRunnable(
0370: animationTickRunnable);
0371: }
0372: }
0373:
0374: /**
0375: * Returns the current document time.
0376: */
0377: public float getCurrentTime() {
0378: boolean p = pauseTime != 0;
0379: unpause();
0380: float t = timedDocumentRoot.getCurrentTime();
0381: if (p) {
0382: pause();
0383: }
0384: return t;
0385: }
0386:
0387: /**
0388: * Sets the current document time.
0389: */
0390: public float setCurrentTime(float t) {
0391: float ret = super .setCurrentTime(t);
0392: if (animationTickRunnable != null) {
0393: animationTickRunnable.resume();
0394: }
0395: return ret;
0396: }
0397:
0398: /**
0399: * Creates a new returns a new TimedDocumentRoot object for the document.
0400: */
0401: protected TimedDocumentRoot createDocumentRoot() {
0402: return new AnimationRoot();
0403: }
0404:
0405: /**
0406: * Starts the animation engine.
0407: */
0408: public void start(long documentStartTime) {
0409: if (started) {
0410: return;
0411: }
0412: started = true;
0413: try {
0414: try {
0415: Calendar cal = Calendar.getInstance();
0416: cal.setTime(new Date(documentStartTime));
0417: timedDocumentRoot.resetDocument(cal);
0418: Object[] bridges = initialBridges.toArray();
0419: initialBridges = null;
0420: for (int i = 0; i < bridges.length; i++) {
0421: SVGAnimationElementBridge bridge = (SVGAnimationElementBridge) bridges[i];
0422: bridge.initializeAnimation();
0423: }
0424: for (int i = 0; i < bridges.length; i++) {
0425: SVGAnimationElementBridge bridge = (SVGAnimationElementBridge) bridges[i];
0426: bridge.initializeTimedElement();
0427: }
0428: // tick(0, false);
0429: // animationThread = new AnimationThread();
0430: // animationThread.start();
0431: UpdateManager um = ctx.getUpdateManager();
0432: if (um != null) {
0433: RunnableQueue q = um.getUpdateRunnableQueue();
0434: animationTickRunnable = new AnimationTickRunnable(
0435: q, this );
0436: q.setIdleRunnable(animationTickRunnable);
0437: }
0438: } catch (AnimationException ex) {
0439: throw new BridgeException(ctx, ex.getElement()
0440: .getElement(), ex.getMessage());
0441: }
0442: } catch (Exception ex) {
0443: if (ctx.getUserAgent() == null) {
0444: ex.printStackTrace();
0445: } else {
0446: ctx.getUserAgent().displayError(ex);
0447: }
0448: }
0449: }
0450:
0451: /**
0452: * Sets the animation limiting mode to "none".
0453: */
0454: public void setAnimationLimitingNone() {
0455: animationLimitingMode = 0;
0456: }
0457:
0458: /**
0459: * Sets the animation limiting mode to a percentage of CPU.
0460: * @param pc the maximum percentage of CPU to use (0 < pc ≤ 1)
0461: */
0462: public void setAnimationLimitingCPU(float pc) {
0463: animationLimitingMode = 1;
0464: animationLimitingAmount = pc;
0465: }
0466:
0467: /**
0468: * Sets the animation limiting mode to a number of frames per second.
0469: * @param fps the maximum number of frames per second (fps > 0)
0470: */
0471: public void setAnimationLimitingFPS(float fps) {
0472: animationLimitingMode = 2;
0473: animationLimitingAmount = fps;
0474: }
0475:
0476: /**
0477: * A class for the root time container.
0478: */
0479: protected class AnimationRoot extends TimedDocumentRoot {
0480:
0481: /**
0482: * Creates a new AnimationRoot object.
0483: */
0484: public AnimationRoot() {
0485: super (!isSVG12, isSVG12);
0486: }
0487:
0488: /**
0489: * Returns the namespace URI of the event that corresponds to the given
0490: * animation event name.
0491: */
0492: protected String getEventNamespaceURI(String eventName) {
0493: if (!isSVG12) {
0494: return null;
0495: }
0496: if (eventName.equals("focusin")
0497: || eventName.equals("focusout")
0498: || eventName.equals("activate")
0499: || animationEventNames12.contains(eventName)) {
0500: return XMLConstants.XML_EVENTS_NAMESPACE_URI;
0501: }
0502: return null;
0503: }
0504:
0505: /**
0506: * Returns the type of the event that corresponds to the given
0507: * animation event name.
0508: */
0509: protected String getEventType(String eventName) {
0510: if (eventName.equals("focusin")) {
0511: return "DOMFocusIn";
0512: } else if (eventName.equals("focusout")) {
0513: return "DOMFocusOut";
0514: } else if (eventName.equals("activate")) {
0515: return "DOMActivate";
0516: }
0517: if (isSVG12) {
0518: if (animationEventNames12.contains(eventName)) {
0519: return eventName;
0520: }
0521: } else {
0522: if (animationEventNames11.contains(eventName)) {
0523: return eventName;
0524: }
0525: }
0526: return null;
0527: }
0528:
0529: /**
0530: * Returns the name of the repeat event.
0531: * @return "repeatEvent" for SVG
0532: */
0533: protected String getRepeatEventName() {
0534: return SMILConstants.SMIL_REPEAT_EVENT_NAME;
0535: }
0536:
0537: /**
0538: * Fires a TimeEvent of the given type on this element.
0539: * @param eventType the type of TimeEvent ("beginEvent", "endEvent"
0540: * or "repeatEvent"/"repeat").
0541: * @param time the timestamp of the event object
0542: */
0543: protected void fireTimeEvent(String eventType, Calendar time,
0544: int detail) {
0545: AnimationSupport.fireTimeEvent((EventTarget) document,
0546: eventType, time, detail);
0547: }
0548:
0549: /**
0550: * Invoked to indicate this timed element became active at the
0551: * specified time.
0552: * @param begin the time the element became active, in document simple time
0553: */
0554: protected void toActive(float begin) {
0555: }
0556:
0557: /**
0558: * Invoked to indicate that this timed element became inactive.
0559: * @param stillActive if true, indicates that the element is still
0560: * actually active, but between the end of the
0561: * computed repeat duration and the end of the
0562: * interval
0563: * @param isFrozen whether the element is frozen or not
0564: */
0565: protected void toInactive(boolean stillActive, boolean isFrozen) {
0566: }
0567:
0568: /**
0569: * Invoked to indicate that this timed element has had its fill removed.
0570: */
0571: protected void removeFill() {
0572: }
0573:
0574: /**
0575: * Invoked to indicate that this timed element has been sampled at the
0576: * given time.
0577: * @param simpleTime the sample time in local simple time
0578: * @param simpleDur the simple duration of the element
0579: * @param repeatIteration the repeat iteration during which the element
0580: * was sampled
0581: */
0582: protected void sampledAt(float simpleTime, float simpleDur,
0583: int repeatIteration) {
0584: }
0585:
0586: /**
0587: * Invoked to indicate that this timed element has been sampled
0588: * at the end of its active time, at an integer multiple of the
0589: * simple duration. This is the "last" value that will be used
0590: * for filling, which cannot be sampled normally.
0591: */
0592: protected void sampledLastValue(int repeatIteration) {
0593: }
0594:
0595: /**
0596: * Returns the timed element with the given ID.
0597: */
0598: protected TimedElement getTimedElementById(String id) {
0599: return AnimationSupport.getTimedElementById(id, document);
0600: }
0601:
0602: /**
0603: * Returns the event target with the given ID.
0604: */
0605: protected EventTarget getEventTargetById(String id) {
0606: return AnimationSupport.getEventTargetById(id, document);
0607: }
0608:
0609: /**
0610: * Returns the target of this animation as an {@link EventTarget}. Used
0611: * for eventbase timing specifiers where the element ID is omitted.
0612: */
0613: protected EventTarget getAnimationEventTarget() {
0614: return null;
0615: }
0616:
0617: /**
0618: * Returns the event target that should be listened to for
0619: * access key events.
0620: */
0621: protected EventTarget getRootEventTarget() {
0622: return (EventTarget) document;
0623: }
0624:
0625: /**
0626: * Returns the DOM element that corresponds to this timed element, if
0627: * such a DOM element exists.
0628: */
0629: public Element getElement() {
0630: return null;
0631: }
0632:
0633: /**
0634: * Returns whether this timed element comes before the given timed
0635: * element in document order.
0636: */
0637: public boolean isBefore(TimedElement other) {
0638: return false;
0639: }
0640:
0641: /**
0642: * Invoked by timed elements in this document to indicate that the
0643: * current interval will be re-evaluated at the next sample.
0644: */
0645: protected void currentIntervalWillUpdate() {
0646: if (animationTickRunnable != null) {
0647: animationTickRunnable.resume();
0648: }
0649: }
0650: }
0651:
0652: /**
0653: * Idle runnable to tick the animation, that reads times from System.in.
0654: */
0655: protected static class DebugAnimationTickRunnable extends
0656: AnimationTickRunnable {
0657:
0658: float t = 0f;
0659:
0660: public DebugAnimationTickRunnable(RunnableQueue q,
0661: SVGAnimationEngine eng) {
0662: super (q, eng);
0663: waitTime = Long.MAX_VALUE;
0664: new Thread() {
0665: public void run() {
0666: java.io.BufferedReader r = new java.io.BufferedReader(
0667: new java.io.InputStreamReader(System.in));
0668: System.out.println("Enter times.");
0669: for (;;) {
0670: String s;
0671: try {
0672: s = r.readLine();
0673: } catch (java.io.IOException e) {
0674: s = null;
0675: }
0676: if (s == null) {
0677: System.exit(0);
0678: }
0679: t = Float.parseFloat(s);
0680: DebugAnimationTickRunnable.this .resume();
0681: }
0682: }
0683: }.start();
0684: }
0685:
0686: public void resume() {
0687: waitTime = 0;
0688: Object lock = q.getIteratorLock();
0689: synchronized (lock) {
0690: lock.notify();
0691: }
0692: }
0693:
0694: public long getWaitTime() {
0695: long wt = waitTime;
0696: waitTime = Long.MAX_VALUE;
0697: return wt;
0698: }
0699:
0700: public void run() {
0701: SVGAnimationEngine eng = getAnimationEngine();
0702: synchronized (eng) {
0703: try {
0704: try {
0705: eng.tick(t, false);
0706: } catch (AnimationException ex) {
0707: throw new BridgeException(eng.ctx, ex
0708: .getElement().getElement(), ex
0709: .getMessage());
0710: }
0711: } catch (Exception ex) {
0712: if (eng.ctx.getUserAgent() == null) {
0713: ex.printStackTrace();
0714: } else {
0715: eng.ctx.getUserAgent().displayError(ex);
0716: }
0717: }
0718: }
0719: }
0720: }
0721:
0722: /**
0723: * Idle runnable to tick the animation.
0724: */
0725: protected static class AnimationTickRunnable implements
0726: RunnableQueue.IdleRunnable {
0727:
0728: /**
0729: * Calendar instance used for passing current time values to the
0730: * animation timing system.
0731: */
0732: protected Calendar time = Calendar.getInstance();
0733:
0734: // /**
0735: // * The current document time in seconds, truncated.
0736: // */
0737: // protected double second = -1.;
0738:
0739: // /**
0740: // * The number of frames that have been ticked so far this second.
0741: // */
0742: // protected int frames;
0743:
0744: /**
0745: * The number of milliseconds to wait until the next animation tick.
0746: * This is returned by {@link #getWaitTime()}.
0747: */
0748: protected long waitTime;
0749:
0750: /**
0751: * The RunnableQueue in which this is the
0752: * {@link RunnableQueue.IdleRunnable}.
0753: */
0754: protected RunnableQueue q;
0755:
0756: /**
0757: * The number of past tick times to keep, for computing the average
0758: * time per tick.
0759: */
0760: private static final int NUM_TIMES = 8;
0761:
0762: /**
0763: * The past tick times.
0764: */
0765: protected long[] times = new long[NUM_TIMES];
0766:
0767: /**
0768: * The sum of the times in {@link #times}.
0769: */
0770: protected long sumTime;
0771:
0772: /**
0773: * The current index into {@link #times}.
0774: */
0775: protected int timeIndex;
0776:
0777: /**
0778: * A weak reference to the SVGAnimationEngine this AnimationTickRunnable
0779: * is for. We makes this a WeakReference so that a ticking animation
0780: * engine does not prevent from being GCed.
0781: */
0782: protected WeakReference engRef;
0783:
0784: /**
0785: * The maximum number of consecutive exceptions to allow before
0786: * stopping the report of them.
0787: */
0788: protected static final int MAX_EXCEPTION_COUNT = 10;
0789:
0790: /**
0791: * The number of consecutive exceptions that have been thrown. This is
0792: * used to detect when exceptions are occurring every tick, and to stop
0793: * reporting them when this happens.
0794: */
0795: protected int exceptionCount;
0796:
0797: /**
0798: * Creates a new AnimationTickRunnable.
0799: */
0800: public AnimationTickRunnable(RunnableQueue q,
0801: SVGAnimationEngine eng) {
0802: this .q = q;
0803: this .engRef = new WeakReference(eng);
0804: // Initialize the past times to 100ms.
0805: Arrays.fill(times, 100);
0806: sumTime = 100 * NUM_TIMES;
0807: }
0808:
0809: /**
0810: * Forces an animation update, if the {@link RunnableQueue} is
0811: * currently waiting.
0812: */
0813: public void resume() {
0814: waitTime = 0;
0815: Object lock = q.getIteratorLock();
0816: synchronized (lock) {
0817: lock.notify();
0818: }
0819: }
0820:
0821: /**
0822: * Returns the system time that can be safely waited until before this
0823: * {@link Runnable} is run again.
0824: *
0825: * @return time to wait until, <code>0</code> if no waiting can
0826: * be done, or {@link Long#MAX_VALUE} if the {@link Runnable}
0827: * should not be run again at this time
0828: */
0829: public long getWaitTime() {
0830: return waitTime;
0831: }
0832:
0833: /**
0834: * Performs one tick of the animation.
0835: */
0836: public void run() {
0837: SVGAnimationEngine eng = getAnimationEngine();
0838: synchronized (eng) {
0839: int animationLimitingMode = eng.animationLimitingMode;
0840: float animationLimitingAmount = eng.animationLimitingAmount;
0841: try {
0842: try {
0843: long before = System.currentTimeMillis();
0844: time.setTime(new Date(before));
0845: float t = eng.timedDocumentRoot
0846: .convertWallclockTime(time);
0847: // if (Math.floor(t) > second) {
0848: // second = Math.floor(t);
0849: // System.err.println("fps: " + frames);
0850: // frames = 0;
0851: // }
0852: float t2 = eng.tick(t, false);
0853: long after = System.currentTimeMillis();
0854: long dur = after - before;
0855: if (dur == 0) {
0856: dur = 1;
0857: }
0858: sumTime -= times[timeIndex];
0859: sumTime += dur;
0860: times[timeIndex] = dur;
0861: timeIndex = (timeIndex + 1) % NUM_TIMES;
0862:
0863: if (t2 == Float.POSITIVE_INFINITY) {
0864: waitTime = Long.MAX_VALUE;
0865: } else {
0866: waitTime = before + (long) (t2 * 1000)
0867: - 1000;
0868: if (waitTime < after) {
0869: waitTime = after;
0870: }
0871: if (animationLimitingMode != 0) {
0872: float ave = (float) sumTime / NUM_TIMES;
0873: float delay;
0874: if (animationLimitingMode == 1) {
0875: // %cpu
0876: delay = ave
0877: / animationLimitingAmount
0878: - ave;
0879: } else {
0880: // fps
0881: delay = 1000f
0882: / animationLimitingAmount
0883: - ave;
0884: }
0885: long newWaitTime = after + (long) delay;
0886: if (newWaitTime > waitTime) {
0887: waitTime = newWaitTime;
0888: }
0889: }
0890: }
0891: // frames++;
0892: } catch (AnimationException ex) {
0893: throw new BridgeException(eng.ctx, ex
0894: .getElement().getElement(), ex
0895: .getMessage());
0896: }
0897: exceptionCount = 0;
0898: } catch (Exception ex) {
0899: if (++exceptionCount < MAX_EXCEPTION_COUNT) {
0900: if (eng.ctx.getUserAgent() == null) {
0901: ex.printStackTrace();
0902: } else {
0903: eng.ctx.getUserAgent().displayError(ex);
0904: }
0905: }
0906: }
0907:
0908: if (animationLimitingMode == 0) {
0909: // so we don't steal too much time from the Swing thread
0910: try {
0911: Thread.sleep(1);
0912: } catch (InterruptedException ie) {
0913: }
0914: }
0915: }
0916: }
0917:
0918: /**
0919: * Returns the SVGAnimationEngine this AnimationTickRunnable is for.
0920: */
0921: protected SVGAnimationEngine getAnimationEngine() {
0922: return (SVGAnimationEngine) engRef.get();
0923: }
0924: }
0925:
0926: /**
0927: * The thread that ticks the animation.
0928: */
0929: protected class AnimationThread extends Thread {
0930:
0931: /**
0932: * The current time.
0933: */
0934: protected Calendar time = Calendar.getInstance();
0935:
0936: /**
0937: * The RunnableQueue to perform the animation in.
0938: */
0939: protected RunnableQueue runnableQueue = ctx.getUpdateManager()
0940: .getUpdateRunnableQueue();
0941:
0942: /**
0943: * The animation ticker Runnable.
0944: */
0945: protected Ticker ticker = new Ticker();
0946:
0947: /**
0948: * Ticks the animation over as fast as possible.
0949: */
0950: public void run() {
0951: if (true) {
0952: for (;;) {
0953: time.setTime(new Date());
0954: ticker.t = timedDocumentRoot
0955: .convertWallclockTime(time);
0956: try {
0957: runnableQueue.invokeAndWait(ticker);
0958: } catch (InterruptedException e) {
0959: return;
0960: }
0961: }
0962: } else {
0963: ticker.t = 1;
0964: while (ticker.t < 10) {
0965: try {
0966: Thread.sleep(1000);
0967: } catch (InterruptedException ie) {
0968: }
0969: try {
0970: runnableQueue.invokeAndWait(ticker);
0971: } catch (InterruptedException e) {
0972: return;
0973: }
0974: ticker.t++;
0975: }
0976: }
0977: }
0978:
0979: /**
0980: * A runnable that ticks the animation engine.
0981: */
0982: protected class Ticker implements Runnable {
0983:
0984: /**
0985: * The document time to tick at next.
0986: */
0987: protected float t;
0988:
0989: /**
0990: * Ticks the animation over.
0991: */
0992: public void run() {
0993: tick(t, false);
0994: }
0995: }
0996: }
0997:
0998: // AnimatableValue factories
0999:
1000: /**
1001: * Interface for AnimatableValue factories.
1002: */
1003: protected interface Factory {
1004:
1005: /**
1006: * Creates a new AnimatableValue from a string.
1007: */
1008: AnimatableValue createValue(AnimationTarget target, String ns,
1009: String ln, boolean isCSS, String s);
1010:
1011: /**
1012: * Creates a new AnimatableValue from a CSS {@link Value}.
1013: */
1014: AnimatableValue createValue(AnimationTarget target, String pn,
1015: Value v);
1016: }
1017:
1018: /**
1019: * Factory class for AnimatableValues for CSS properties.
1020: * XXX Shorthand properties are not supported.
1021: */
1022: protected abstract class CSSValueFactory implements Factory {
1023:
1024: public AnimatableValue createValue(AnimationTarget target,
1025: String ns, String ln, boolean isCSS, String s) {
1026: // XXX Always parsing as a CSS value.
1027: return createValue(target, ln,
1028: createCSSValue(target, ln, s));
1029: }
1030:
1031: public AnimatableValue createValue(AnimationTarget target,
1032: String pn, Value v) {
1033: CSSStylableElement elt = (CSSStylableElement) target
1034: .getElement();
1035: v = computeValue(elt, pn, v);
1036: return createAnimatableValue(target, pn, v);
1037: }
1038:
1039: /**
1040: * Creates a new AnimatableValue from a CSS {@link Value}, after
1041: * computation and inheritance.
1042: */
1043: protected abstract AnimatableValue createAnimatableValue(
1044: AnimationTarget target, String pn, Value v);
1045:
1046: /**
1047: * Creates a new CSS {@link Value} from a string.
1048: */
1049: protected Value createCSSValue(AnimationTarget t, String pn,
1050: String s) {
1051: CSSStylableElement elt = (CSSStylableElement) t
1052: .getElement();
1053: Value v = cssEngine.parsePropertyValue(elt, pn, s);
1054: return computeValue(elt, pn, v);
1055: }
1056:
1057: /**
1058: * Computes a CSS {@link Value} and performance inheritance if the
1059: * specified value is 'inherit'.
1060: */
1061: protected Value computeValue(CSSStylableElement elt, String pn,
1062: Value v) {
1063: ValueManager[] vms = cssEngine.getValueManagers();
1064: int idx = cssEngine.getPropertyIndex(pn);
1065: if (idx != -1) {
1066: if (v.getCssValueType() == CSSValue.CSS_INHERIT) {
1067: elt = CSSEngine.getParentCSSStylableElement(elt);
1068: if (elt != null) {
1069: return cssEngine.getComputedStyle(elt, null,
1070: idx);
1071: }
1072: return vms[idx].getDefaultValue();
1073: }
1074: v = vms[idx].computeValue(elt, null, cssEngine, idx,
1075: dummyStyleMap, v);
1076: }
1077: return v;
1078: }
1079: }
1080:
1081: /**
1082: * Factory class for {@link AnimatableBooleanValue}s.
1083: */
1084: protected class AnimatableBooleanValueFactory implements Factory {
1085:
1086: /**
1087: * Creates a new AnimatableValue from a string.
1088: */
1089: public AnimatableValue createValue(AnimationTarget target,
1090: String ns, String ln, boolean isCSS, String s) {
1091: return new AnimatableBooleanValue(target, "true".equals(s));
1092: }
1093:
1094: /**
1095: * Creates a new AnimatableValue from a CSS {@link Value}.
1096: */
1097: public AnimatableValue createValue(AnimationTarget target,
1098: String pn, Value v) {
1099: return new AnimatableBooleanValue(target, "true".equals(v
1100: .getCssText()));
1101: }
1102: }
1103:
1104: /**
1105: * Factory class for {@link AnimatableIntegerValue}s.
1106: */
1107: protected class AnimatableIntegerValueFactory implements Factory {
1108:
1109: /**
1110: * Creates a new AnimatableValue from a string.
1111: */
1112: public AnimatableValue createValue(AnimationTarget target,
1113: String ns, String ln, boolean isCSS, String s) {
1114: return new AnimatableIntegerValue(target, Integer
1115: .parseInt(s));
1116: }
1117:
1118: /**
1119: * Creates a new AnimatableValue from a CSS {@link Value}.
1120: */
1121: public AnimatableValue createValue(AnimationTarget target,
1122: String pn, Value v) {
1123: return new AnimatableIntegerValue(target, Math.round(v
1124: .getFloatValue()));
1125: }
1126: }
1127:
1128: /**
1129: * Factory class for {@link AnimatableNumberValue}s.
1130: */
1131: protected class AnimatableNumberValueFactory implements Factory {
1132:
1133: /**
1134: * Creates a new AnimatableValue from a string.
1135: */
1136: public AnimatableValue createValue(AnimationTarget target,
1137: String ns, String ln, boolean isCSS, String s) {
1138: return new AnimatableNumberValue(target, Float
1139: .parseFloat(s));
1140: }
1141:
1142: /**
1143: * Creates a new AnimatableValue from a CSS {@link Value}.
1144: */
1145: public AnimatableValue createValue(AnimationTarget target,
1146: String pn, Value v) {
1147: return new AnimatableNumberValue(target, v.getFloatValue());
1148: }
1149: }
1150:
1151: /**
1152: * Factory class for {@link AnimatableNumberOrPercentageValue}s.
1153: */
1154: protected class AnimatableNumberOrPercentageValueFactory implements
1155: Factory {
1156:
1157: /**
1158: * Creates a new AnimatableValue from a string.
1159: */
1160: public AnimatableValue createValue(AnimationTarget target,
1161: String ns, String ln, boolean isCSS, String s) {
1162: float v;
1163: boolean pc;
1164: if (s.charAt(s.length() - 1) == '%') {
1165: v = Float.parseFloat(s.substring(0, s.length() - 1));
1166: pc = true;
1167: } else {
1168: v = Float.parseFloat(s);
1169: pc = false;
1170: }
1171: return new AnimatableNumberOrPercentageValue(target, v, pc);
1172: }
1173:
1174: /**
1175: * Creates a new AnimatableValue from a CSS {@link Value}.
1176: */
1177: public AnimatableValue createValue(AnimationTarget target,
1178: String pn, Value v) {
1179: switch (v.getPrimitiveType()) {
1180: case CSSPrimitiveValue.CSS_PERCENTAGE:
1181: return new AnimatableNumberOrPercentageValue(target, v
1182: .getFloatValue(), true);
1183: case CSSPrimitiveValue.CSS_NUMBER:
1184: return new AnimatableNumberOrPercentageValue(target, v
1185: .getFloatValue());
1186: }
1187: // XXX Do something better than returning null.
1188: return null;
1189: }
1190: }
1191:
1192: /**
1193: * Factory class for {@link AnimatablePreserveAspectRatioValue}s.
1194: */
1195: protected class AnimatablePreserveAspectRatioValueFactory implements
1196: Factory {
1197:
1198: /**
1199: * The parsed 'align' value.
1200: */
1201: protected short align;
1202:
1203: /**
1204: * The parsed 'meetOrSlice' value.
1205: */
1206: protected short meetOrSlice;
1207:
1208: /**
1209: * Parser for preserveAspectRatio values.
1210: */
1211: protected PreserveAspectRatioParser parser = new PreserveAspectRatioParser();
1212:
1213: /**
1214: * Handler for the preserveAspectRatio parser.
1215: */
1216: protected DefaultPreserveAspectRatioHandler handler = new DefaultPreserveAspectRatioHandler() {
1217:
1218: /**
1219: * Implements {@link
1220: * PreserveAspectRatioHandler#startPreserveAspectRatio()}.
1221: */
1222: public void startPreserveAspectRatio()
1223: throws ParseException {
1224: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_UNKNOWN;
1225: meetOrSlice = SVGPreserveAspectRatio.SVG_MEETORSLICE_UNKNOWN;
1226: }
1227:
1228: /**
1229: * Implements {@link PreserveAspectRatioHandler#none()}.
1230: */
1231: public void none() throws ParseException {
1232: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE;
1233: }
1234:
1235: /**
1236: * Implements {@link PreserveAspectRatioHandler#xMaxYMax()}.
1237: */
1238: public void xMaxYMax() throws ParseException {
1239: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX;
1240: }
1241:
1242: /**
1243: * Implements {@link PreserveAspectRatioHandler#xMaxYMid()}.
1244: */
1245: public void xMaxYMid() throws ParseException {
1246: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID;
1247: }
1248:
1249: /**
1250: * Implements {@link PreserveAspectRatioHandler#xMaxYMin()}.
1251: */
1252: public void xMaxYMin() throws ParseException {
1253: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMIN;
1254: }
1255:
1256: /**
1257: * Implements {@link PreserveAspectRatioHandler#xMidYMax()}.
1258: */
1259: public void xMidYMax() throws ParseException {
1260: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMAX;
1261: }
1262:
1263: /**
1264: * Implements {@link PreserveAspectRatioHandler#xMidYMid()}.
1265: */
1266: public void xMidYMid() throws ParseException {
1267: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID;
1268: }
1269:
1270: /**
1271: * Implements {@link PreserveAspectRatioHandler#xMidYMin()}.
1272: */
1273: public void xMidYMin() throws ParseException {
1274: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN;
1275: }
1276:
1277: /**
1278: * Implements {@link PreserveAspectRatioHandler#xMinYMax()}.
1279: */
1280: public void xMinYMax() throws ParseException {
1281: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMAX;
1282: }
1283:
1284: /**
1285: * Implements {@link PreserveAspectRatioHandler#xMinYMid()}.
1286: */
1287: public void xMinYMid() throws ParseException {
1288: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID;
1289: }
1290:
1291: /**
1292: * Implements {@link PreserveAspectRatioHandler#xMinYMin()}.
1293: */
1294: public void xMinYMin() throws ParseException {
1295: align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN;
1296: }
1297:
1298: /**
1299: * Implements {@link PreserveAspectRatioHandler#meet()}.
1300: */
1301: public void meet() throws ParseException {
1302: meetOrSlice = SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET;
1303: }
1304:
1305: /**
1306: * Implements {@link PreserveAspectRatioHandler#slice()}.
1307: */
1308: public void slice() throws ParseException {
1309: meetOrSlice = SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE;
1310: }
1311: };
1312:
1313: /**
1314: * Creates a new AnimatablePreserveAspectRatioValueFactory.
1315: */
1316: public AnimatablePreserveAspectRatioValueFactory() {
1317: parser.setPreserveAspectRatioHandler(handler);
1318: }
1319:
1320: /**
1321: * Creates a new AnimatableValue from a string.
1322: */
1323: public AnimatableValue createValue(AnimationTarget target,
1324: String ns, String ln, boolean isCSS, String s) {
1325: try {
1326: parser.parse(s);
1327: return new AnimatablePreserveAspectRatioValue(target,
1328: align, meetOrSlice);
1329: } catch (ParseException e) {
1330: // XXX Do something better than returning null.
1331: return null;
1332: }
1333: }
1334:
1335: /**
1336: * Creates a new AnimatableValue from a CSS {@link Value}. Returns null
1337: * since preserveAspectRatio values aren't used in CSS values.
1338: */
1339: public AnimatableValue createValue(AnimationTarget target,
1340: String pn, Value v) {
1341: return null;
1342: }
1343: }
1344:
1345: /**
1346: * Factory class for {@link AnimatableLengthValue}s.
1347: */
1348: protected class AnimatableLengthValueFactory implements Factory {
1349:
1350: /**
1351: * The parsed length unit type.
1352: */
1353: protected short type;
1354:
1355: /**
1356: * The parsed length value.
1357: */
1358: protected float value;
1359:
1360: /**
1361: * Parser for lengths.
1362: */
1363: protected LengthParser parser = new LengthParser();
1364:
1365: /**
1366: * Handler for the length parser.
1367: */
1368: protected LengthHandler handler = new DefaultLengthHandler() {
1369: public void startLength() throws ParseException {
1370: type = SVGLength.SVG_LENGTHTYPE_NUMBER;
1371: }
1372:
1373: public void lengthValue(float v) throws ParseException {
1374: value = v;
1375: }
1376:
1377: public void em() throws ParseException {
1378: type = SVGLength.SVG_LENGTHTYPE_EMS;
1379: }
1380:
1381: public void ex() throws ParseException {
1382: type = SVGLength.SVG_LENGTHTYPE_EXS;
1383: }
1384:
1385: public void in() throws ParseException {
1386: type = SVGLength.SVG_LENGTHTYPE_IN;
1387: }
1388:
1389: public void cm() throws ParseException {
1390: type = SVGLength.SVG_LENGTHTYPE_CM;
1391: }
1392:
1393: public void mm() throws ParseException {
1394: type = SVGLength.SVG_LENGTHTYPE_MM;
1395: }
1396:
1397: public void pc() throws ParseException {
1398: type = SVGLength.SVG_LENGTHTYPE_PC;
1399: }
1400:
1401: public void pt() throws ParseException {
1402: type = SVGLength.SVG_LENGTHTYPE_PT;
1403: }
1404:
1405: public void px() throws ParseException {
1406: type = SVGLength.SVG_LENGTHTYPE_PX;
1407: }
1408:
1409: public void percentage() throws ParseException {
1410: type = SVGLength.SVG_LENGTHTYPE_PERCENTAGE;
1411: }
1412:
1413: public void endLength() throws ParseException {
1414: }
1415: };
1416:
1417: /**
1418: * Creates a new AnimatableLengthValueFactory.
1419: */
1420: public AnimatableLengthValueFactory() {
1421: parser.setLengthHandler(handler);
1422: }
1423:
1424: /**
1425: * Creates a new AnimatableValue from a string.
1426: */
1427: public AnimatableValue createValue(AnimationTarget target,
1428: String ns, String ln, boolean isCSS, String s) {
1429: short pcInterp = target.getPercentageInterpretation(ns, ln,
1430: isCSS);
1431: try {
1432: parser.parse(s);
1433: return new AnimatableLengthValue(target, type, value,
1434: pcInterp);
1435: } catch (ParseException e) {
1436: // XXX Do something better than returning null.
1437: return null;
1438: }
1439: }
1440:
1441: /**
1442: * Creates a new AnimatableValue from a CSS {@link Value}.
1443: */
1444: public AnimatableValue createValue(AnimationTarget target,
1445: String pn, Value v) {
1446: return new AnimatableIntegerValue(target, Math.round(v
1447: .getFloatValue()));
1448: }
1449: }
1450:
1451: /**
1452: * Factory class for {@link AnimatableLengthListValue}s.
1453: */
1454: protected class AnimatableLengthListValueFactory implements Factory {
1455:
1456: /**
1457: * Parser for length lists.
1458: */
1459: protected LengthListParser parser = new LengthListParser();
1460:
1461: /**
1462: * The producer class that accumulates the lengths.
1463: */
1464: protected LengthArrayProducer producer = new LengthArrayProducer();
1465:
1466: /**
1467: * Creates a new AnimatableLengthListValueFactory.
1468: */
1469: public AnimatableLengthListValueFactory() {
1470: parser.setLengthListHandler(producer);
1471: }
1472:
1473: /**
1474: * Creates a new AnimatableValue from a string.
1475: */
1476: public AnimatableValue createValue(AnimationTarget target,
1477: String ns, String ln, boolean isCSS, String s) {
1478: try {
1479: short pcInterp = target.getPercentageInterpretation(ns,
1480: ln, isCSS);
1481: parser.parse(s);
1482: return new AnimatableLengthListValue(target, producer
1483: .getLengthTypeArray(), producer
1484: .getLengthValueArray(), pcInterp);
1485: } catch (ParseException e) {
1486: // XXX Do something better than returning null.
1487: return null;
1488: }
1489: }
1490:
1491: /**
1492: * Creates a new AnimatableValue from a CSS {@link Value}. Returns null
1493: * since point lists aren't used in CSS values.
1494: */
1495: public AnimatableValue createValue(AnimationTarget target,
1496: String pn, Value v) {
1497: return null;
1498: }
1499: }
1500:
1501: /**
1502: * Factory class for {@link AnimatableNumberListValue}s.
1503: */
1504: protected class AnimatableNumberListValueFactory implements Factory {
1505:
1506: /**
1507: * Parser for number lists.
1508: */
1509: protected NumberListParser parser = new NumberListParser();
1510:
1511: /**
1512: * The producer class that accumulates the numbers.
1513: */
1514: protected FloatArrayProducer producer = new FloatArrayProducer();
1515:
1516: /**
1517: * Creates a new AnimatableNumberListValueFactory.
1518: */
1519: public AnimatableNumberListValueFactory() {
1520: parser.setNumberListHandler(producer);
1521: }
1522:
1523: /**
1524: * Creates a new AnimatableValue from a string.
1525: */
1526: public AnimatableValue createValue(AnimationTarget target,
1527: String ns, String ln, boolean isCSS, String s) {
1528: try {
1529: parser.parse(s);
1530: return new AnimatableNumberListValue(target, producer
1531: .getFloatArray());
1532: } catch (ParseException e) {
1533: // XXX Do something better than returning null.
1534: return null;
1535: }
1536: }
1537:
1538: /**
1539: * Creates a new AnimatableValue from a CSS {@link Value}. Returns null
1540: * since number lists aren't used in CSS values.
1541: */
1542: public AnimatableValue createValue(AnimationTarget target,
1543: String pn, Value v) {
1544: return null;
1545: }
1546: }
1547:
1548: /**
1549: * Factory class for {@link AnimatablePointListValue}s.
1550: */
1551: protected class AnimatablePointListValueFactory implements Factory {
1552:
1553: /**
1554: * Parser for point lists.
1555: */
1556: protected PointsParser parser = new PointsParser();
1557:
1558: /**
1559: * The producer class that accumulates the points.
1560: */
1561: protected FloatArrayProducer producer = new FloatArrayProducer();
1562:
1563: /**
1564: * Creates a new AnimatablePointListValueFactory.
1565: */
1566: public AnimatablePointListValueFactory() {
1567: parser.setPointsHandler(producer);
1568: }
1569:
1570: /**
1571: * Creates a new AnimatableValue from a string.
1572: */
1573: public AnimatableValue createValue(AnimationTarget target,
1574: String ns, String ln, boolean isCSS, String s) {
1575: try {
1576: parser.parse(s);
1577: return new AnimatablePointListValue(target, producer
1578: .getFloatArray());
1579: } catch (ParseException e) {
1580: // XXX Do something better than returning null.
1581: return null;
1582: }
1583: }
1584:
1585: /**
1586: * Creates a new AnimatableValue from a CSS {@link Value}. Returns null
1587: * since point lists aren't used in CSS values.
1588: */
1589: public AnimatableValue createValue(AnimationTarget target,
1590: String pn, Value v) {
1591: return null;
1592: }
1593: }
1594:
1595: /**
1596: * Factory class for {@link AnimatablePathDataValue}s.
1597: */
1598: protected class AnimatablePathDataFactory implements Factory {
1599:
1600: /**
1601: * Parser for path data.
1602: */
1603: protected PathParser parser = new PathParser();
1604:
1605: /**
1606: * The producer class that accumulates the path segments.
1607: */
1608: protected PathArrayProducer producer = new PathArrayProducer();
1609:
1610: /**
1611: * Creates a new AnimatablePathDataFactory.
1612: */
1613: public AnimatablePathDataFactory() {
1614: parser.setPathHandler(producer);
1615: }
1616:
1617: /**
1618: * Creates a new AnimatableValue from a string.
1619: */
1620: public AnimatableValue createValue(AnimationTarget target,
1621: String ns, String ln, boolean isCSS, String s) {
1622: try {
1623: parser.parse(s);
1624: return new AnimatablePathDataValue(target, producer
1625: .getPathCommands(), producer
1626: .getPathParameters());
1627: } catch (ParseException e) {
1628: // XXX Do something better than returning null.
1629: return null;
1630: }
1631: }
1632:
1633: /**
1634: * Creates a new AnimatableValue from a CSS {@link Value}. Returns null
1635: * since point lists aren't used in CSS values.
1636: */
1637: public AnimatableValue createValue(AnimationTarget target,
1638: String pn, Value v) {
1639: return null;
1640: }
1641: }
1642:
1643: /**
1644: * Factory class for {@link AnimatableStringValue}s.
1645: */
1646: protected class UncomputedAnimatableStringValueFactory implements
1647: Factory {
1648:
1649: public AnimatableValue createValue(AnimationTarget target,
1650: String ns, String ln, boolean isCSS, String s) {
1651: return new AnimatableStringValue(target, s);
1652: }
1653:
1654: public AnimatableValue createValue(AnimationTarget target,
1655: String pn, Value v) {
1656: return new AnimatableStringValue(target, v.getCssText());
1657: }
1658: }
1659:
1660: /**
1661: * Factory class for {@link AnimatableLengthOrIdentValue}s.
1662: */
1663: protected class AnimatableLengthOrIdentFactory extends
1664: CSSValueFactory {
1665:
1666: protected AnimatableValue createAnimatableValue(
1667: AnimationTarget target, String pn, Value v) {
1668: if (v instanceof StringValue) {
1669: return new AnimatableLengthOrIdentValue(target, v
1670: .getStringValue());
1671: }
1672: short pcInterp = target.getPercentageInterpretation(null,
1673: pn, true);
1674: FloatValue fv = (FloatValue) v;
1675: return new AnimatableLengthOrIdentValue(target, fv
1676: .getPrimitiveType(), fv.getFloatValue(), pcInterp);
1677: }
1678: }
1679:
1680: /**
1681: * Factory class for {@link AnimatableNumberOrIdentValue}s.
1682: */
1683: protected class AnimatableNumberOrIdentFactory extends
1684: CSSValueFactory {
1685:
1686: /**
1687: * Whether numbers are actually numeric keywords, as with the
1688: * font-weight property.
1689: */
1690: protected boolean numericIdents;
1691:
1692: public AnimatableNumberOrIdentFactory(boolean numericIdents) {
1693: this .numericIdents = numericIdents;
1694: }
1695:
1696: protected AnimatableValue createAnimatableValue(
1697: AnimationTarget target, String pn, Value v) {
1698: if (v instanceof StringValue) {
1699: return new AnimatableNumberOrIdentValue(target, v
1700: .getStringValue());
1701: }
1702: FloatValue fv = (FloatValue) v;
1703: return new AnimatableNumberOrIdentValue(target, fv
1704: .getFloatValue(), numericIdents);
1705: }
1706: }
1707:
1708: /**
1709: * Factory class for {@link AnimatableAngleValue}s.
1710: */
1711: protected class AnimatableAngleValueFactory extends CSSValueFactory {
1712:
1713: protected AnimatableValue createAnimatableValue(
1714: AnimationTarget target, String pn, Value v) {
1715: FloatValue fv = (FloatValue) v;
1716: short unit;
1717: switch (fv.getPrimitiveType()) {
1718: case CSSPrimitiveValue.CSS_NUMBER:
1719: case CSSPrimitiveValue.CSS_DEG:
1720: unit = SVGAngle.SVG_ANGLETYPE_DEG;
1721: break;
1722: case CSSPrimitiveValue.CSS_RAD:
1723: unit = SVGAngle.SVG_ANGLETYPE_RAD;
1724: break;
1725: case CSSPrimitiveValue.CSS_GRAD:
1726: unit = SVGAngle.SVG_ANGLETYPE_GRAD;
1727: break;
1728: default:
1729: // XXX Do something better than returning null.
1730: return null;
1731: }
1732: return new AnimatableAngleValue(target, fv.getFloatValue(),
1733: unit);
1734: }
1735: }
1736:
1737: /**
1738: * Factory class for {@link AnimatableAngleOrIdentValue}s.
1739: */
1740: protected class AnimatableAngleOrIdentFactory extends
1741: CSSValueFactory {
1742:
1743: protected AnimatableValue createAnimatableValue(
1744: AnimationTarget target, String pn, Value v) {
1745: if (v instanceof StringValue) {
1746: return new AnimatableAngleOrIdentValue(target, v
1747: .getStringValue());
1748: }
1749: FloatValue fv = (FloatValue) v;
1750: short unit;
1751: switch (fv.getPrimitiveType()) {
1752: case CSSPrimitiveValue.CSS_NUMBER:
1753: case CSSPrimitiveValue.CSS_DEG:
1754: unit = SVGAngle.SVG_ANGLETYPE_DEG;
1755: break;
1756: case CSSPrimitiveValue.CSS_RAD:
1757: unit = SVGAngle.SVG_ANGLETYPE_RAD;
1758: break;
1759: case CSSPrimitiveValue.CSS_GRAD:
1760: unit = SVGAngle.SVG_ANGLETYPE_GRAD;
1761: break;
1762: default:
1763: // XXX Do something better than returning null.
1764: return null;
1765: }
1766: return new AnimatableAngleOrIdentValue(target, fv
1767: .getFloatValue(), unit);
1768: }
1769: }
1770:
1771: /**
1772: * Factory class for {@link AnimatableColorValue}s.
1773: */
1774: protected class AnimatableColorValueFactory extends CSSValueFactory {
1775:
1776: protected AnimatableValue createAnimatableValue(
1777: AnimationTarget target, String pn, Value v) {
1778: Paint p = PaintServer.convertPaint(target.getElement(),
1779: null, v, 1.0f, ctx);
1780: if (p instanceof Color) {
1781: Color c = (Color) p;
1782: return new AnimatableColorValue(target,
1783: c.getRed() / 255f, c.getGreen() / 255f, c
1784: .getBlue() / 255f);
1785: }
1786: // XXX Indicate that the parsed value wasn't a Color?
1787: return null;
1788: }
1789: }
1790:
1791: /**
1792: * Factory class for {@link AnimatablePaintValue}s.
1793: */
1794: protected class AnimatablePaintValueFactory extends CSSValueFactory {
1795:
1796: /**
1797: * Creates a new {@link AnimatablePaintValue} from a {@link Color}
1798: * object.
1799: */
1800: protected AnimatablePaintValue createColorPaintValue(
1801: AnimationTarget t, Color c) {
1802: return AnimatablePaintValue.createColorPaintValue(t, c
1803: .getRed() / 255f, c.getGreen() / 255f,
1804: c.getBlue() / 255f);
1805:
1806: }
1807:
1808: protected AnimatableValue createAnimatableValue(
1809: AnimationTarget target, String pn, Value v) {
1810: if (v.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
1811: switch (v.getPrimitiveType()) {
1812: case CSSPrimitiveValue.CSS_IDENT:
1813: return AnimatablePaintValue
1814: .createNonePaintValue(target);
1815: case CSSPrimitiveValue.CSS_RGBCOLOR: {
1816: Paint p = PaintServer.convertPaint(target
1817: .getElement(), null, v, 1.0f, ctx);
1818: return createColorPaintValue(target, (Color) p);
1819: }
1820: case CSSPrimitiveValue.CSS_URI:
1821: return AnimatablePaintValue.createURIPaintValue(
1822: target, v.getStringValue());
1823: }
1824: } else {
1825: Value v1 = v.item(0);
1826: switch (v1.getPrimitiveType()) {
1827: case CSSPrimitiveValue.CSS_RGBCOLOR: {
1828: Paint p = PaintServer.convertPaint(target
1829: .getElement(), null, v, 1.0f, ctx);
1830: return createColorPaintValue(target, (Color) p);
1831: }
1832: case CSSPrimitiveValue.CSS_URI: {
1833: Value v2 = v.item(1);
1834: switch (v2.getPrimitiveType()) {
1835: case CSSPrimitiveValue.CSS_IDENT:
1836: return AnimatablePaintValue
1837: .createURINonePaintValue(target, v1
1838: .getStringValue());
1839: case CSSPrimitiveValue.CSS_RGBCOLOR: {
1840: Paint p = PaintServer.convertPaint(target
1841: .getElement(), null, v.item(1), 1.0f,
1842: ctx);
1843: return createColorPaintValue(target, (Color) p);
1844: }
1845: }
1846: }
1847: }
1848: }
1849: // XXX Indicate that the specified Value wasn't a Color?
1850: return null;
1851: }
1852: }
1853:
1854: /**
1855: * Factory class for computed CSS {@link AnimatableStringValue}s.
1856: */
1857: protected class AnimatableStringValueFactory extends
1858: CSSValueFactory {
1859:
1860: protected AnimatableValue createAnimatableValue(
1861: AnimationTarget target, String pn, Value v) {
1862: return new AnimatableStringValue(target, v.getCssText());
1863: }
1864: }
1865: }
|