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.j2d.Box;
029: import com.sun.perseus.j2d.GraphicsProperties;
030: import com.sun.perseus.j2d.PathSupport;
031: import com.sun.perseus.j2d.PaintTarget;
032: import com.sun.perseus.j2d.RenderGraphics;
033: import com.sun.perseus.j2d.Tile;
034:
035: import com.sun.perseus.util.SVGConstants;
036:
037: import org.w3c.dom.DOMException;
038:
039: import org.w3c.dom.svg.SVGMatrix;
040: import org.w3c.dom.svg.SVGRect;
041:
042: import com.sun.perseus.j2d.Transform;
043:
044: /**
045: * Typical base class for nodes which render something (shapes and images).
046: *
047: * @version $Id: AbstractRenderingNode.java,v 1.13 2006/06/29 10:47:28 ln156897 Exp $
048: */
049: public abstract class AbstractRenderingNode extends
050: CompositeGraphicsNode implements Transformable {
051: /**
052: * The Transform applied to this node.
053: */
054: protected Transform transform;
055:
056: /**
057: * The motion transform applied to this node. This is typically used for
058: * animateMotion, but it can be used as a regular trait as well.
059: */
060: protected Transform motion;
061:
062: /**
063: * Used to track the node's rendering area and the rendered areas.
064: */
065: protected RenderingManager renderingManager;
066:
067: /**
068: * Constructor.
069: *
070: * @param ownerDocument this element's owner <code>DocumentNode</code>
071: */
072: public AbstractRenderingNode(final DocumentNode ownerDocument) {
073: super (ownerDocument);
074:
075: if (DirtyAreaManager.ON) {
076: renderingManager = new RenderingManager(this );
077: }
078: }
079:
080: /**
081: * Clears the text layouts, if any exist. This is typically
082: * called when the font selection has changed and nodes such
083: * as <code>Text</code> should recompute their layouts.
084: * This should recursively call clearLayouts on children
085: * node or expanded content, if any.
086: */
087: protected void clearLayouts() {
088: }
089:
090: /**
091: * Computes this node's rendering tile.
092: *
093: * @param tile the Tile instance whose bounds should be set.
094: * @return the device space rendering tile.
095: */
096: protected final void computeRenderingTile(final Tile tile) {
097: computeRenderingTile(tile, txf, this );
098: }
099:
100: /**
101: * Computes the rendering tile for the given set of GraphicsProperties.
102: *
103: * @param tile the Tile instance whose bounds should be set.
104: * @param t the Transform to the requested tile space, from this node's user
105: * space.
106: * @param gp the <code>GraphicsProperties</code> for which the tile
107: * should be computed.
108: * @return the screen bounding box when this node is rendered with the
109: * given render context.
110: */
111: abstract void computeRenderingTile(final Tile tile,
112: final Transform t, final GraphicsProperties gp);
113:
114: /**
115: * @return an adequate <code>ElementNodeProxy</code> for this node.
116: */
117: ElementNodeProxy buildProxy() {
118: return new AbstractRenderingNodeProxy(this );
119: }
120:
121: /**
122: * Returns the <code>ModelNode</code>, if any, hit by the
123: * point at coordinate x/y.
124: *
125: * @param pt the x/y coordinate. Should never be null and be
126: * of size two. If not, the behavior is unspecified.
127: * The coordinates are in viewport space.
128: * @return the <tt>ModelNode</tt> hit at the given point or null
129: * if none was hit.
130: */
131: public ModelNode nodeHitAt(final float[] pt) {
132: // If a node does not render, it is never hit
133: if ((canRenderState != 0) || !isHitVP(pt)) {
134: return null;
135: }
136:
137: return this ;
138: }
139:
140: /**
141: * Returns true if this node is hit by the input point. The input point
142: * is in viewport space.
143: *
144: * @return true if the node is hit by the input point.
145: * @see #nodeHitAt
146: */
147: abstract boolean isHitVP(float[] pt);
148:
149: /**
150: * Returns true if this proxy node is hit by the input point. The input
151: * point is in viewport space.
152: *
153: * @param pt the x/y coordinate. Should never be null and be
154: * of size two. If not, the behavior is unspecified.
155: * The x/y coordinate is in viewport space.
156: * @param proxy the tested ElementNodeProxy.
157: * @return true if the node is hit by the input point.
158: * @see #isHitVP
159: */
160: abstract boolean isProxyHitVP(float[] pt,
161: final AbstractRenderingNodeProxy proxy);
162:
163: /**
164: * Returns the <code>ModelNode</code>, if any, hit by the
165: * point at coordinate x/y in the proxy tree starting at
166: * proxy.
167: *
168: * @param pt the x/y coordinate. Should never be null and be
169: * of size two. If not, the behavior is unspecified.
170: * The coordinates are in viewport space.
171: * @param proxy the root of the proxy tree to test.
172: * @return the <tt>ModelNode</tt> hit at the given point or null
173: * if none was hit.
174: */
175: ModelNode proxyNodeHitAt(final float[] pt,
176: final ElementNodeProxy proxy) {
177: // If a node does not render, it is never hit
178: if ((canRenderState != 0)
179: || !isProxyHitVP(pt, (AbstractRenderingNodeProxy) proxy)) {
180: return null;
181: }
182:
183: return proxy;
184: }
185:
186: /**
187: * @param newDisplay the new computed display value
188: */
189: void setComputedDisplay(final boolean newDisplay) {
190: super .setComputedDisplay(newDisplay);
191:
192: renderingDirty();
193: }
194:
195: /**
196: * @param newVisibility the new computed visibility property.
197: */
198: void setComputedVisibility(final boolean newVisibility) {
199: super .setComputedVisibility(newVisibility);
200:
201: renderingDirty();
202: }
203:
204: /**
205: * Paints this node into the input <code>RenderGraphics</code>.
206: *
207: * @param rg the <tt>RenderGraphics</tt> where the node should paint itself
208: */
209: public void paint(final RenderGraphics rg) {
210: if ((canRenderState != 0)) {
211: return;
212: }
213:
214: if (DirtyAreaManager.ON) {
215: Tile primitiveTile = getRenderingTile();
216: if (primitiveTile == null
217: || rg.getRenderingTile().isHit(primitiveTile)) {
218: // rg.setPrimitiveTile(primitiveTile);
219: paintRendered(rg, this , this , txf);
220:
221: // nodeRendered is called seperately from paintRendered
222: // because paintRendered is used in different contexts,
223: // for example by proxy nodes to render, using their
224: // proxied node's paintRendered method.
225: nodeRendered();
226: }
227: } else {
228: paintRendered(rg, this , this , txf);
229: }
230: }
231:
232: /**
233: * @param bbox the bounding box to which this node's bounding box should be
234: * appended. That bounding box is in the target coordinate space. It
235: * may be null, in which case this node should create a new one.
236: * @param t the transform to apply from the node's coordinate space to the
237: * target coordinate space. May be null for the identity
238: * transform.
239: * @return the node's bounding box in the target coordinate space.
240: */
241: Box addBBox(Box bbox, final Transform t) {
242: return addNodeBBox(bbox, t);
243: }
244:
245: /**
246: * @return the tight bounding box in current user coordinate
247: * space.
248: */
249: public SVGRect getBBox() {
250: return addNodeBBox(null, null);
251: }
252:
253: /**
254: * @return the tight bounding box in screen coordinate space.
255: */
256: public SVGRect getScreenBBox() {
257: // There is no screen bounding box if the element is not hooked
258: // into the main tree.
259: if (!inDocumentTree()) {
260: return null;
261: }
262:
263: return addNodeBBox(null, txf);
264: }
265:
266: /**
267: * Paints this node into the input RenderGraphics.
268: *
269: * @param rg this node is painted into this <tt>RenderGraphics</tt>
270: * @param gp the <code>GraphicsProperties</code> controlling the operation's
271: * rendering
272: * @param pt the <code>PaintTarget</code> for the paint operation.
273: * @param txf the <code>Transform</code> from user space to device space for
274: * the paint operation.
275: */
276: abstract void paintRendered(final RenderGraphics rg,
277: final GraphicsProperties gp, final PaintTarget pt,
278: final Transform tx);
279:
280: /**
281: * Should be called whenever this node's rendering becomes dirty.
282: */
283: final void renderingDirty() {
284: if (DirtyAreaManager.ON) {
285: renderingManager.dirty();
286: }
287: }
288:
289: /**
290: * @return the number of properties on this node.
291: */
292: public int getNumberOfProperties() {
293: return NUMBER_OF_PROPERTIES;
294: }
295:
296: /**
297: * Called when the computed value of the given property has changed.
298: * On a rendering node, as we do not render regular children nor expanded
299: * content, we do not propagate property state changes.
300: *
301: * @param propertyIndex index for the property whose value has changed.
302: * @param parentPropertyValue the value that children of this node should
303: * now inherit.
304: */
305: protected void propagatePropertyState(final int propertyIndex,
306: final Object parentPropertyValue) {
307: // Propagate to proxies.
308: if (firstProxy != null) {
309: ElementNodeProxy proxy = firstProxy;
310: while (proxy != null) {
311: ((CompositeGraphicsNodeProxy) proxy)
312: .proxiedPropertyStateChange(propertyIndex,
313: parentPropertyValue);
314: proxy = proxy.nextProxy;
315: }
316: }
317: }
318:
319: /**
320: * Called when the computed value of the given float property has changed.
321: * On a rendering node, as we do not render regular children nor expanded
322: * content, we do not propagate property state changes.
323: *
324: * @param propertyIndex index for the property whose value has changed.
325: * @param parentPropertyValue the value that children of this node should
326: * now inherit.
327: */
328: protected void propagateFloatPropertyState(final int propertyIndex,
329: final float parentPropertyValue) {
330: // Propagate to proxies.
331: if (firstProxy != null) {
332: ElementNodeProxy proxy = firstProxy;
333: while (proxy != null) {
334: ((CompositeGraphicsNodeProxy) proxy)
335: .proxiedFloatPropertyStateChange(propertyIndex,
336: parentPropertyValue);
337: proxy = proxy.nextProxy;
338: }
339: }
340: }
341:
342: /**
343: * Called when the computed value of the given packed property has changed.
344: * On a rendering node, as we do not render regular children nor expanded
345: * content, we do not propagate property state changes.
346: *
347: * @param propertyIndex index for the property whose value has changed.
348: * @param parentPropertyValue the value that children of this node should
349: * now inherit.
350: */
351: protected void propagatePackedPropertyState(
352: final int propertyIndex, final int parentPropertyValue) {
353: // Propagate to proxies.
354: if (firstProxy != null) {
355: ElementNodeProxy proxy = firstProxy;
356: while (proxy != null) {
357: ((CompositeGraphicsNodeProxy) proxy)
358: .proxiedPackedPropertyStateChange(
359: propertyIndex, parentPropertyValue);
360: proxy = proxy.nextProxy;
361: }
362: }
363: }
364:
365: /**
366: * Recomputes the transform cache, if one exists. This should recursively
367: * call recomputeTransformState on children node or expanded content, if
368: * any child is rendered down below.
369: *
370: * @param parentTransform the Transform applied to this node's parent.
371: */
372: protected void recomputeTransformState(
373: final Transform parentTransform) {
374: txf = appendTransform(parentTransform, txf);
375: inverseTxf = null;
376: computeCanRenderTransformBit(txf);
377: renderingDirty();
378: }
379:
380: /**
381: * @param newTransform The new <code>Transformable</code>'s transform.
382: */
383: public void setTransform(final Transform newTransform) {
384: if (equal(transform, newTransform)) {
385: return;
386: }
387: modifyingNode();
388: this .transform = newTransform;
389: recomputeTransformState();
390: recomputeProxyTransformState();
391: modifiedNode();
392: }
393:
394: /**
395: * @param newMotion The new motion transform.
396: */
397: public void setMotion(final Transform newMotion) {
398: if (equal(newMotion, motion)) {
399: return;
400: }
401:
402: modifyingNode();
403: this .motion = newMotion;
404: recomputeTransformState();
405: recomputeProxyTransformState();
406: modifiedNode();
407: }
408:
409: /**
410: * @return This <code>Transformable</code>'s transform.
411: */
412: public Transform getTransform() {
413: return transform;
414: }
415:
416: /**
417: * @return This node's motion transform.
418: */
419: public Transform getMotion() {
420: return motion;
421: }
422:
423: /**
424: * @return the bounding box, in screen coordinate, which encompasses the
425: * node's rendering.
426: */
427: protected Tile getRenderingTile() {
428: return renderingManager.getRenderingTile();
429: }
430:
431: /**
432: * @return the tile which encompasses the node's last actual rendering. If
433: * this node's hasRendering method returns false, then this method should
434: * return null. By default, this method returns null because
435: * hasNodeRendering returns null by default.
436: */
437: protected Tile getLastRenderedTile() {
438: return renderingManager.getLastRenderedTile();
439: }
440:
441: /**
442: * After calling this method, getLastRenderedTile should always return null.
443: */
444: protected void clearLastRenderedTile() {
445: renderingManager.clearLastRenderedTile();
446: }
447:
448: /**
449: * To be overriddent by derived classes, such as TimedElementNode,
450: * if they need to do special operations when hooked into the
451: * document tree.
452: */
453: void nodeHookedInDocumentTree() {
454: super .nodeHookedInDocumentTree();
455: renderingDirty();
456: }
457:
458: /**
459: * To be overriddent by derived classes, such as TimedElementNode,
460: * if they need to do special operations when unhooked from the
461: * document tree.
462: */
463: void nodeUnhookedFromDocumentTree() {
464: super .nodeUnhookedFromDocumentTree();
465: renderingDirty();
466: }
467:
468: /**
469: * Appends this node's transform, if it is not null.
470: *
471: * @param tx the <code>Transform</code> to apply additional node
472: * transforms to. This may be null.
473: * @param workTx a <code>Transform</code> which can be re-used if a
474: * new <code>Transform</code> needs to be created and workTx
475: * is not the same instance as tx.
476: * @return a transform with this node's transform added.
477: */
478: protected Transform appendTransform(Transform tx, Transform workTx) {
479: if (transform == null && motion == null) {
480: return tx;
481: }
482:
483: tx = recycleTransform(tx, workTx);
484:
485: if (motion != null) {
486: tx.mMultiply(motion);
487: }
488:
489: if (transform != null) {
490: tx.mMultiply(transform);
491: }
492:
493: return tx;
494: }
495:
496: /**
497: * An <code>AbstractRenderingNode</code> has something to render
498: *
499: * @return true
500: */
501: public boolean hasNodeRendering() {
502: return true;
503: }
504:
505: /**
506: * Simply notifies the RenderingManager.
507: */
508: protected void nodeRendered() {
509: if (DirtyAreaManager.ON) {
510: renderingManager.rendered();
511: }
512: }
513:
514: /**
515: * AbstractShapeNode handles the transform attribute.
516: *
517: * @param traitName the name of the trait which the element may support.
518: * @return true if this element supports the given trait in one of the
519: * trait accessor methods.
520: */
521: boolean supportsTrait(final String traitName) {
522: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE == traitName
523: || SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE == traitName) {
524: return true;
525: } else {
526: return super .supportsTrait(traitName);
527: }
528: }
529:
530: /**
531: * AbstractShapeNode handles the transform attribute.
532: * Other attributes are handled by the super class.
533: *
534: * @param name the name of the requested trait.
535: * @return the value of the requested trait, as a string.
536: *
537: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
538: * trait is not supported on this element or null.
539: * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
540: * trait's computed value cannot be converted to a String (SVG Tiny only).
541: */
542: public String getTraitImpl(final String name) throws DOMException {
543: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE == name) {
544: return toStringTrait(transform);
545: } else if (SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE == name) {
546: return toStringTrait(motion);
547: } else {
548: return super .getTraitImpl(name);
549: }
550: }
551:
552: /**
553: * AbstractShapeNode handles the transform attribute.
554: * Other attributes are handled by the super class.
555: *
556: * @param name matrix trait name.
557: * @return the trait value corresponding to name as SVGMatrix.
558: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
559: * trait is not supported on this element or null.
560: * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
561: * trait's computed value cannot be converted to {@link
562: * org.w3c.dom.svg.SVGMatrix SVGMatrix}
563: */
564: SVGMatrix getMatrixTraitImpl(String name) throws DOMException {
565: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE.equals(name)) {
566: return toSVGMatrixTrait(transform);
567: } else if (SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE
568: .equals(name)) {
569: return toSVGMatrixTrait(motion);
570: } else {
571: return super .getMatrixTraitImpl(name);
572: }
573: }
574:
575: /**
576: * @param traitName the trait name.
577: */
578: TraitAnim createTraitAnimImpl(final String traitName) {
579: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE == traitName) {
580: return new TransformTraitAnim(this , traitName);
581: } else if (SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE == traitName) {
582: return new MotionTraitAnim(this , traitName);
583: } else {
584: return super .createTraitAnimImpl(traitName);
585: }
586: }
587:
588: /**
589: * Set the trait value as float array.
590: *
591: * @param name the trait's name.
592: * @param value the trait's value.
593: *
594: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
595: * trait is not supported on this element.
596: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
597: * trait's value cannot be specified as a float
598: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
599: * value is an invalid value for the given trait.
600: */
601: void setFloatArrayTrait(final String name, final float[][] value)
602: throws DOMException {
603: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE == name) {
604: if (transform == null) {
605: modifyingNode();
606: transform = new Transform(value[0][0], value[1][0],
607: value[2][0], value[3][0], value[4][0],
608: value[5][0]);
609: } else {
610: if (!transform.equals(value)) {
611: modifyingNode();
612: transform.setTransform(value[0][0], value[1][0],
613: value[2][0], value[3][0], value[4][0],
614: value[5][0]);
615: } else {
616: return;
617: }
618: }
619: recomputeTransformState();
620: recomputeProxyTransformState();
621: modifiedNode();
622: } else if (SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE == name) {
623: if (motion == null) {
624: modifyingNode();
625: motion = new Transform(value[0][0], value[1][0],
626: value[2][0], value[3][0], value[4][0],
627: value[5][0]);
628: } else {
629: if (!motion.equals(value)) {
630: modifyingNode();
631: motion.setTransform(value[0][0], value[1][0],
632: value[2][0], value[3][0], value[4][0],
633: value[5][0]);
634: } else {
635: return;
636: }
637: }
638: recomputeTransformState();
639: recomputeProxyTransformState();
640: modifiedNode();
641: } else {
642: super .setFloatArrayTrait(name, value);
643: }
644: }
645:
646: /**
647: * Validates the input trait value.
648: *
649: * @param traitName the name of the trait to be validated.
650: * @param value the value to be validated
651: * @param reqNamespaceURI the namespace of the element requesting
652: * validation.
653: * @param reqLocalName the local name of the element requesting validation.
654: * @param reqTraitNamespace the namespace of the trait which has the values
655: * value on the requesting element.
656: * @param reqTraitName the name of the trait which has the values value on
657: * the requesting element.
658: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
659: * value is incompatible with the given trait.
660: */
661: public float[][] validateFloatArrayTrait(final String traitName,
662: final String value, final String reqNamespaceURI,
663: final String reqLocalName, final String reqTraitNamespace,
664: final String reqTraitName) throws DOMException {
665: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE == traitName
666: || SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE == traitName) {
667: Transform txf = parseTransformTrait(traitName, value);
668: return new float[][] { { (float) txf.getComponent(0) },
669: { (float) txf.getComponent(1) },
670: { (float) txf.getComponent(2) },
671: { (float) txf.getComponent(3) },
672: { (float) txf.getComponent(4) },
673: { (float) txf.getComponent(5) } };
674: } else {
675: return super .validateFloatArrayTrait(traitName, value,
676: reqNamespaceURI, reqLocalName, reqTraitNamespace,
677: reqTraitName);
678: }
679: }
680:
681: /**
682: * AbstractShapeNode handles the transform attribute.
683: * Other attributes are handled by the super class.
684: *
685: * @param name the name of the trait to set
686: * @param value the string value for the trait to set.
687: *
688: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
689: * trait is not supported on this element or null.
690: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
691: * trait's value cannot be specified as a String
692: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
693: * value is an invalid value for the given trait or null.
694: * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if
695: * attempt is made to change readonly trait.
696: */
697: public void setTraitImpl(final String name, final String value)
698: throws DOMException {
699: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE == name) {
700: setTransform(parseTransformTrait(name, value));
701: } else if (SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE == name) {
702: setMotion(parseTransformTrait(name, value));
703: } else {
704: super .setTraitImpl(name, value);
705: }
706: }
707:
708: /**
709: * AbstractShapeNode handles the transform attribute.
710: * Other attributes are handled by the super class.
711: *
712: * @param name name of trait to set
713: * @param matrix Transform value of trait
714: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
715: * trait is not supported on this element or null.
716: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
717: * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGMatrix
718: * SVGMatrix}
719: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
720: * value is an invalid value for the given trait or null.
721: * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if
722: * attempt is made to change readonly trait.
723: */
724: void setMatrixTraitImpl(final String name, final Transform matrix)
725: throws DOMException {
726: // We use .equals for the transform attribute as the string may not
727: // have been interned. We use == for the motion pseudo attribute because
728: // it is only used internally and from the SVGConstants strings.
729: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE.equals(name)) {
730: setTransform(matrix);
731: } else if (SVGConstants.SVG_MOTION_PSEUDO_ATTRIBUTE == name) {
732: setMotion(matrix);
733: } else {
734: super .setMatrixTraitImpl(name, matrix);
735: }
736: }
737:
738: /**
739: * @param name the name of the trait to convert.
740: * @param value the float trait value to convert.
741: */
742: String toStringTrait(final String name, final float[][] value) {
743: if (SVGConstants.SVG_TRANSFORM_ATTRIBUTE == name) {
744: Transform transform = new Transform(value[0][0],
745: value[1][0], value[2][0], value[3][0], value[4][0],
746: value[5][0]);
747: return toStringTrait(transform);
748: } else {
749: return super.toStringTrait(name, value);
750: }
751: }
752:
753: }
|