FileDocCategorySizeDatePackage
CompositeGraphicsNode.javaAPI DocphoneME MR2 API (J2ME)105405Wed May 02 18:00:36 BST 2007com.sun.perseus.model

CompositeGraphicsNode.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.perseus.model;

import org.w3c.dom.DOMException;

import org.w3c.dom.svg.SVGRect;
import org.w3c.dom.svg.SVGRGBColor;
import org.w3c.dom.svg.SVGLocatableElement;

import com.sun.perseus.j2d.GraphicsProperties;
import com.sun.perseus.j2d.Transform;

import com.sun.perseus.util.SVGConstants;

import com.sun.perseus.j2d.RGB;
import com.sun.perseus.j2d.PaintDef;
import com.sun.perseus.j2d.PaintTarget;
import com.sun.perseus.j2d.PaintServer;

/**
 * <code>CompositeGraphicsNode</code> is the base class for all nodes which are
 * both composites (i.e., they can have children) and have graphic properties
 * (such as <code>fill</code> or <code>stroke</code>).
 *
 * @version $Id: CompositeGraphicsNode.java,v 1.16 2006/06/29 10:47:29 ln156897 Exp $
 */
public abstract class CompositeGraphicsNode extends ElementNode 
        implements GraphicsNode, PaintTarget, SVGLocatableElement {
    // =======================================================================
    // Constants used for packing and unpacking data in the pack member
    // =======================================================================
    protected static final int FONT_STYLE_MASK =       
            0x60000000;    /* 30-29 */
    protected static final int FONT_WEIGHT_MASK =      
            0x1E000000;    /* 28-27-26-25 */
    protected static final int TEXT_ANCHOR_MASK =      
            0x01800000;    /* 24-23 */
    protected static final int STROKE_OPACITY_MASK =   
            0x007F8000;    /* 22-21-20-19-18-17-16-15 */
    protected static final int FILL_OPACITY_MASK =     
            0x00007F80;    /* 14-13-12-11-10-09-08-07 */
    protected static final int FILL_RULE_MASK =        
            0x00000040;    /* 06 */
    protected static final int STROKE_LINE_CAP_MASK =  
            0x00000030;    /* 05-04 */
    protected static final int STROKE_LINE_JOIN_MASK = 
            0x0000000C;    /* 03-02 */
    protected static final int VISIBILITY_MASK =       
            0x00000002;    /* 01 */
    protected static final int DISPLAY_MASK =         
            0x00000001;    /* 00 */

    // =======================================================================
    // Constants used for packing and unpacking data in the pack2 member
    // =======================================================================
    protected static final int OPACITY_MASK =   
            0x000000FF;    /* 7-6-5-4-3-2-1-0 */

    protected static final int FONT_STYLE_NORMAL_IMPL =  0x00000000;
    protected static final int FONT_STYLE_ITALIC_IMPL =  0x40000000;
    protected static final int FONT_STYLE_OBLIQUE_IMPL = 0x60000000;

    protected static final int FONT_WEIGHT_100_IMPL     = 0x00000000;
    protected static final int FONT_WEIGHT_200_IMPL     = 0x02000000;
    protected static final int FONT_WEIGHT_300_IMPL     = 0x04000000;
    protected static final int FONT_WEIGHT_400_IMPL     = 0x06000000;
    protected static final int FONT_WEIGHT_500_IMPL     = 0x08000000;
    protected static final int FONT_WEIGHT_600_IMPL     = 0x0A000000;
    protected static final int FONT_WEIGHT_700_IMPL     = 0x0C000000;
    protected static final int FONT_WEIGHT_800_IMPL     = 0x0E000000;
    protected static final int FONT_WEIGHT_900_IMPL     = 0x10000000;
    protected static final int FONT_WEIGHT_LIGHTER_IMPL = 0x12000000;
    protected static final int FONT_WEIGHT_BOLDER_IMPL  = 0x14000000;

    protected static final int TEXT_ANCHOR_MIDDLE_IMPL = 0x00000000;
    protected static final int TEXT_ANCHOR_START_IMPL  = 0x00800000;
    protected static final int TEXT_ANCHOR_END_IMPL =    0x01000000;

    protected static final int CAP_BUTT_IMPL =   0x00000000;
    protected static final int CAP_ROUND_IMPL =  0x00000010;
    protected static final int CAP_SQUARE_IMPL = 0x00000020;

    protected static final int JOIN_MITER_IMPL = 0x00000000;
    protected static final int JOIN_ROUND_IMPL = 0x00000004;
    protected static final int JOIN_BEVEL_IMPL = 0x00000008;

    // ====================================================================
    // Property values
    // ====================================================================
    /**
     * The current color. The fill and stroke colors may be
     * relative to the current color
     */
    protected RGB color = INITIAL_COLOR;

    /**
     * The fill paint used to fill this node
     */
    protected PaintServer fill = INITIAL_FILL;

    /**
     * The stroke paint used to stroke the outline of
     * this ShapeNode
     */
    protected PaintServer stroke = INITIAL_STROKE;

    /**
     * The stroke width
     */
    protected float strokeWidth = INITIAL_STROKE_WIDTH;

    /**
     * The stroke miter limit
     */
    protected float strokeMiterLimit = INITIAL_STROKE_MITER_LIMIT;

    /**
     * The stroke dash array
     */
    protected float[] strokeDashArray = INITIAL_STROKE_DASH_ARRAY;

    /**
     * The stroke dash offset
     */
    protected float strokeDashOffset = INITIAL_STROKE_DASH_OFFSET;

    // 
    // Property pack
    //
    // fontStyle:     3 styles,  2 bits
    // fontWeight:    9 weights, 4 bits
    // textAnchor     3 values,  2 bits
    // strokeOpacity:            8 bits [0-255]
    // fillOpacity:              8 bits [0-255]
    // fillRule:                 1 bits
    // strokeLineCap: 3 values,  2 bits
    // strokeLineJoin 3 values,  2 bits
    // visibility: 2 values,     1 bit
    // display: 2 values,        1 bit
    // Total:                   31 bits
    // 
    // Encoding:
    //
    // fontStyle: 30-29
    // fontWeight: 28-27-26-25
    // textAnchor: 24-23
    // strokeOpacity: 22-21-20-19-18-17-16-15
    // fillOpacity: 14-13-12-11-10-09-08-07
    // fillRule: 06
    // strokeLineCap: 05-04
    // strokeLineJoin: 03-02
    // visibility: 01
    // display: 00
    //
    protected int pack = INITIAL_PACK;
    
    protected static final int INITIAL_FONT_STYLE_IMPL = FONT_STYLE_NORMAL_IMPL;
    protected static final int INITIAL_FONT_WEIGHT_IMPL = FONT_WEIGHT_400_IMPL;
    protected static final int INITIAL_TEXT_ANCHOR_IMPL = 
            TEXT_ANCHOR_START_IMPL;
    protected static final int INITIAL_STROKE_OPACITY_IMPL = 200 << 15;
    protected static final int INITIAL_FILL_OPACITY_IMPL = 200 << 7;
    protected static final int INITIAL_FILL_RULE_IMPL = FILL_RULE_MASK;
    protected static final int INITIAL_STROKE_LINE_CAP_IMPL = CAP_BUTT_IMPL;
    protected static final int INITIAL_STROKE_LINE_JOIN_IMPL = JOIN_MITER_IMPL;
    protected static final int INITIAL_VISIBILITY_IMPL = 0x2;
    protected static final int INITIAL_DISPLAY_IMPL = 0x1;
    protected static final int INITIAL_PACK = 
        INITIAL_FONT_STYLE_IMPL
        |
        INITIAL_FONT_WEIGHT_IMPL
        |
        INITIAL_TEXT_ANCHOR_IMPL
        |
        INITIAL_STROKE_OPACITY_IMPL
        |
        INITIAL_FILL_OPACITY_IMPL
        |
        INITIAL_FILL_RULE_IMPL
        |
        INITIAL_STROKE_LINE_CAP_IMPL
        |
        INITIAL_STROKE_LINE_JOIN_IMPL
        |
        INITIAL_VISIBILITY_IMPL
        |
        INITIAL_DISPLAY_IMPL;

    // 
    // Property pack2
    //
    // opacity:                  8 bits [0-255]
    // Total:                    8 bits
    // 
    // Encoding:
    //
    // opacity: 07-06-05-04-03-02-01-00
    //
    protected int pack2 = INITIAL_PACK2;    

    protected static final int INITIAL_OPACITY_IMPL = 200;
    protected static final int INITIAL_PACK2 = 
        INITIAL_OPACITY_IMPL;

    // ====================================================================
    // Property value types (inherited, relative, none, specific)
    // ====================================================================

    /**
     * Markers are used to keep track of inherited properties, color relative
     * properties and bolder/lighter font weights.
     *
     * 0-20  : property inheritance
     * 21-22 : color relative
     * 23    : is bolder marker
     * 24    : is lighter marker
     */
    protected int markers = DEFAULT_INHERITANCE 
                            | TextNode.DEFAULT_INHERITANCE 
                            | DEFAULT_COLOR_RELATIVE;
    
    // ====================================================================
    // ModelNode Implementation
    // ====================================================================

    /**
     * Cached Transform. May point to the parent transform.
     */
    protected Transform txf = null;

    /**
     * Cached inverse transform. May point to the parent inverse transform.
     */
    protected Transform inverseTxf = null;

    /**
     * Constructor.
     *
     * @param ownerDocument this element's owner <code>DocumentNode</code>
     */
    public CompositeGraphicsNode(final DocumentNode ownerDocument) {
        super(ownerDocument);
        
        // By default, a CompositeGraphicsNode is renderable
        canRenderState &= CAN_RENDER_RENDERABLE_MASK;
    }

    /**
     * A <code>CompositeGraphicsNode</code> contributes to its parent bounding
     * box only if its display property is turned on.
     *
     * @return true if the node's bounding box should be accounted for.
     */
    protected boolean contributeBBox() {
        return (pack & DISPLAY_MASK) != 0;
    }

    // JAVADOC COMMENT ELIDED
    public SVGRect getBBox() {
        return null;
    }

    // JAVADOC COMMENT ELIDED
    public SVGRect getScreenBBox() {
        return null;
    }

    /**
     * Returns the value of the given Object-valued property.
     *
     * @return the value of the given Object-valued property.
     */
    protected Object getPropertyState(final int propertyIndex) {
        switch (propertyIndex) {
        case PROPERTY_FILL:
            return fill;
        case PROPERTY_STROKE:
            return stroke;
        case PROPERTY_COLOR:
            return color;
        case PROPERTY_STROKE_DASH_ARRAY:            
            return strokeDashArray;
        default: 
            return super.getPropertyState(propertyIndex);
        }
    }

    /**
     * Returns the value of the given float-valued property.
     *
     * @return the value of the given property.
     */
    protected float getFloatPropertyState(final int propertyIndex) {
        switch (propertyIndex) {
        case PROPERTY_STROKE_WIDTH:
            return strokeWidth;
        case PROPERTY_STROKE_MITER_LIMIT:
            return strokeMiterLimit;
        case PROPERTY_STROKE_DASH_OFFSET:
            return strokeDashOffset;
        default: 
            return super.getFloatPropertyState(propertyIndex);
        }
    }

    /**
     * Returns the value for the given packed property.
     *
     * @return the value for the given property.
     */
    protected int getPackedPropertyState(final int propertyIndex) {
        switch (propertyIndex) {
        case GraphicsNode.PROPERTY_FILL_RULE:
            return pack & CompositeGraphicsNode.FILL_RULE_MASK;
        case GraphicsNode.PROPERTY_STROKE_LINE_JOIN:
            return pack & CompositeGraphicsNode.STROKE_LINE_JOIN_MASK;
        case GraphicsNode.PROPERTY_STROKE_LINE_CAP:
            return pack & CompositeGraphicsNode.STROKE_LINE_CAP_MASK;
        case GraphicsNode.PROPERTY_DISPLAY:
            return pack & CompositeGraphicsNode.DISPLAY_MASK;
        case GraphicsNode.PROPERTY_VISIBILITY:
            return pack & CompositeGraphicsNode.VISIBILITY_MASK;
        case GraphicsNode.PROPERTY_FILL_OPACITY:
            return pack & CompositeGraphicsNode.FILL_OPACITY_MASK;
        case GraphicsNode.PROPERTY_STROKE_OPACITY:
            return pack & CompositeGraphicsNode.STROKE_OPACITY_MASK;
        case GraphicsNode.PROPERTY_OPACITY:
            return pack2 & CompositeGraphicsNode.OPACITY_MASK;
        default: 
            return super.getPackedPropertyState(propertyIndex);
        }
    }

    /**
     * Sets the computed value of the given Object-valued property.
     *
     * @param propertyIndex the property index
     * @param propertyValue the computed value of the property.
     */
    protected void setPropertyState(final int propertyIndex,
                                    final Object propertyValue) {
        switch (propertyIndex) {
        case PROPERTY_FILL:
            setComputedFill((PaintServer) propertyValue);
            break;
        case PROPERTY_STROKE:
            setComputedStroke((PaintServer) propertyValue);
            break;
        case PROPERTY_COLOR:
            setComputedColor((RGB) propertyValue);
            break;
        case PROPERTY_STROKE_DASH_ARRAY:            
            setComputedStrokeDashArray((float[]) propertyValue);
            break;
        default: 
            super.setPropertyState(propertyIndex, propertyValue);
            break;
        }
    }

    /**
     * Sets the computed value of the given float-valued property.
     *
     * @param propertyIndex the property index
     * @param propertyValue the computed value of the property.
     */
    protected void setFloatPropertyState(final int propertyIndex,
                                          final float propertyValue) {
        switch (propertyIndex) {
        case PROPERTY_STROKE_WIDTH:
            setComputedStrokeWidth(propertyValue);
            break;
        case PROPERTY_STROKE_MITER_LIMIT:
            setComputedStrokeMiterLimit(propertyValue);
            break;
        case PROPERTY_STROKE_DASH_OFFSET:
            setComputedStrokeDashOffset(propertyValue);
            break;
        default: 
            super.setFloatPropertyState(propertyIndex, propertyValue);
            break;
        }
    }

    /**
     * Sets the computed value of the given packed property.
     *
     * @param propertyIndex the property index
     * @param propertyValue the computed value of the property.
     */
    protected void setPackedPropertyState(final int propertyIndex,
                                          final int propertyValue) {
        switch (propertyIndex) {
        case GraphicsNode.PROPERTY_FILL_RULE:
            if (propertyValue == 0) {
                setComputedFillRule(WIND_EVEN_ODD);
            } else {
                setComputedFillRule(WIND_NON_ZERO);
            }
            break;
        case GraphicsNode.PROPERTY_STROKE_LINE_JOIN:
            switch (propertyValue) {
                case CompositeGraphicsNode.JOIN_MITER_IMPL:
                    setComputedStrokeLineJoin(JOIN_MITER);
                    break;
                case CompositeGraphicsNode.JOIN_ROUND_IMPL:
                    setComputedStrokeLineJoin(JOIN_ROUND);
                    break;
                case CompositeGraphicsNode.JOIN_BEVEL_IMPL:
                default:
                    setComputedStrokeLineJoin(JOIN_BEVEL);
                    break;
            }
            break;
        case GraphicsNode.PROPERTY_STROKE_LINE_CAP:
            switch (propertyValue) {
                case CompositeGraphicsNode.CAP_BUTT_IMPL:
                    setComputedStrokeLineCap(CAP_BUTT);
                    break;
                case CompositeGraphicsNode.CAP_ROUND_IMPL:
                    setComputedStrokeLineCap(CAP_ROUND);
                    break;
                case CompositeGraphicsNode.CAP_SQUARE_IMPL:
                default:
                    setComputedStrokeLineCap(CAP_SQUARE);
                    break;
            }
            break;
        case GraphicsNode.PROPERTY_DISPLAY:
            if (propertyValue != 0) {
                setComputedDisplay(true);
            } else {
                setComputedDisplay(false);
            }
            break;
        case GraphicsNode.PROPERTY_VISIBILITY:
            if (propertyValue != 0) {
                setComputedVisibility(true);
            } else {
                setComputedVisibility(false);
            }
            break;
        case GraphicsNode.PROPERTY_FILL_OPACITY:
            setComputedFillOpacity((propertyValue >> 7) / 200.0f);
            break;
        case GraphicsNode.PROPERTY_STROKE_OPACITY:
            setComputedStrokeOpacity((propertyValue >> 15) / 200.0f);
            break;
        case GraphicsNode.PROPERTY_OPACITY:
            setComputedOpacity(propertyValue / 200.0f);
            break;
        default: 
            super.setPackedPropertyState(propertyIndex, propertyValue);
            break;
        }
    }

    /**
     * Checks the state of the Object-valued property.
     *
     * @param propertyIndex the property index
     * @param propertyValue the computed value of the property.
     */
    protected boolean isPropertyState(final int propertyIndex,
                                      final Object propertyValue) {
        switch (propertyIndex) {
        case GraphicsNode.PROPERTY_FILL:
            return fill == propertyValue;
        case GraphicsNode.PROPERTY_STROKE:
            return stroke == propertyValue;
        case GraphicsNode.PROPERTY_COLOR:
            return color == propertyValue;
        case GraphicsNode.PROPERTY_STROKE_DASH_ARRAY:            
            return strokeDashArray == propertyValue;
        default: 
            return super.isPropertyState(propertyIndex, propertyValue);
        }
    }
    
    /**
     * Checks the state of the float property value.
     *
     * @param propertyIndex the property index
     * @param propertyValue the computed value of the property.
     */
    protected boolean isFloatPropertyState(final int propertyIndex,
                                           final float propertyValue) {
        switch (propertyIndex) {
        case GraphicsNode.PROPERTY_STROKE_WIDTH:
            return strokeWidth == propertyValue;
        case GraphicsNode.PROPERTY_STROKE_MITER_LIMIT:
            return strokeMiterLimit == propertyValue;
        case GraphicsNode.PROPERTY_STROKE_DASH_OFFSET:
            return strokeDashOffset == propertyValue;
        default: 
            return super.isFloatPropertyState(propertyIndex, propertyValue);
        }
    }
    
    /**
     * Checks the state of the packed property value.
     *
     * @param propertyIndex the property index
     * @param propertyValue the computed value of the property.
     */
    protected boolean isPackedPropertyState(final int propertyIndex,
                                            final int propertyValue) {
        switch (propertyIndex) {
        case GraphicsNode.PROPERTY_FILL_RULE:
            return (propertyValue
                    ==
                    (pack & CompositeGraphicsNode.FILL_RULE_MASK));
        case GraphicsNode.PROPERTY_STROKE_LINE_JOIN:
            return (propertyValue
                    ==
                    (pack & CompositeGraphicsNode.STROKE_LINE_JOIN_MASK));
        case GraphicsNode.PROPERTY_STROKE_LINE_CAP:
            return (propertyValue
                    ==
                    (pack & CompositeGraphicsNode.STROKE_LINE_CAP_MASK));
        case GraphicsNode.PROPERTY_DISPLAY:
            return (propertyValue
                    ==
                    (pack & CompositeGraphicsNode.DISPLAY_MASK));
        case GraphicsNode.PROPERTY_VISIBILITY:
            return (propertyValue
                    ==
                    (pack & CompositeGraphicsNode.VISIBILITY_MASK));
        case GraphicsNode.PROPERTY_FILL_OPACITY:
            return (propertyValue
                    ==
                    (pack & CompositeGraphicsNode.FILL_OPACITY_MASK));
        case GraphicsNode.PROPERTY_STROKE_OPACITY:
            return (propertyValue
                    ==
                    (pack & CompositeGraphicsNode.STROKE_OPACITY_MASK));
        case GraphicsNode.PROPERTY_OPACITY:
            return (propertyValue
                    ==
                    (pack2 & CompositeGraphicsNode.OPACITY_MASK));
        default: 
            return super.isPackedPropertyState(propertyIndex, propertyValue);
        }
    }

    /**
     * Recomputes all inherited properties.
     */
    void recomputeInheritedProperties() {
        ModelNode p = ownerDocument;
        if (parent != null) {
            p = parent;
        }
        recomputePropertyState(PROPERTY_FILL, 
                               p.getPropertyState(PROPERTY_FILL));
        recomputePropertyState(PROPERTY_STROKE, 
                               p.getPropertyState(PROPERTY_STROKE));
        recomputePropertyState(PROPERTY_COLOR, 
                               p.getPropertyState(PROPERTY_COLOR));
        recomputePackedPropertyState(PROPERTY_FILL_RULE, 
                               p.getPackedPropertyState(PROPERTY_FILL_RULE));
        recomputeFloatPropertyState(PROPERTY_STROKE_WIDTH,
                               p.getFloatPropertyState(PROPERTY_STROKE_WIDTH));
        recomputePackedPropertyState(PROPERTY_STROKE_LINE_JOIN, 
                               p.getPackedPropertyState(
                                    PROPERTY_STROKE_LINE_JOIN));
        recomputePackedPropertyState(PROPERTY_STROKE_LINE_CAP,
                               p.getPackedPropertyState(
                                    PROPERTY_STROKE_LINE_CAP));
        recomputeFloatPropertyState(PROPERTY_STROKE_MITER_LIMIT,
                               p.getFloatPropertyState(
                                    PROPERTY_STROKE_MITER_LIMIT));
        recomputePropertyState(PROPERTY_STROKE_DASH_ARRAY,
                               p.getPropertyState(PROPERTY_STROKE_DASH_ARRAY));
        recomputeFloatPropertyState(PROPERTY_STROKE_DASH_OFFSET, 
                               p.getFloatPropertyState(
                                    PROPERTY_STROKE_DASH_OFFSET));
        recomputePackedPropertyState(PROPERTY_DISPLAY, 
                               p.getPackedPropertyState(PROPERTY_DISPLAY));
        recomputePackedPropertyState(PROPERTY_VISIBILITY, 
                               p.getPackedPropertyState(PROPERTY_VISIBILITY));
        recomputePackedPropertyState(PROPERTY_FILL_OPACITY, 
                               p.getPackedPropertyState(PROPERTY_FILL_OPACITY));
        recomputePackedPropertyState(PROPERTY_STROKE_OPACITY, 
                               p.getPackedPropertyState(
                                     PROPERTY_STROKE_OPACITY));
        recomputePackedPropertyState(PROPERTY_OPACITY, 
                               p.getPackedPropertyState(
                                     PROPERTY_OPACITY));
    }

    /**
     * Recomputes the given Object-valued property's state given the
     * new parent property.
     *
     * @param propertyIndex index for the property whose value is changing.
     * @param parentPropertyValue the value that children of this node should 
     *        now inherit.
     * 
     */
    protected void recomputePropertyState(final int propertyIndex,
                                          final Object parentPropertyValue) {
        // We do not need to recompute the property value if:
        // - the property is _not_ inherited
        // or
        // - the property is inherited by the new parent property computed value
        //   is the same as the current value.
        if (!isInherited(propertyIndex) 
            || 
            isPropertyState(propertyIndex, parentPropertyValue)) {
            // If the property is color relative, the propagation happens
            // through the color property changes.  This means that with
            // currentColor, we inherit the computed value, not the specified
            // currentColor indirection.
            return;
        }

        setPropertyState(propertyIndex, parentPropertyValue);
        propagatePropertyState(propertyIndex, parentPropertyValue);
    }

    /**
     * Recomputes the given float-valued property's state given the new parent 
     * property.
     *
     * @param propertyIndex index for the property whose value is changing.
     * @param parentPropertyValue the value that children of this node should 
     *        now inherit.
     * 
     */
    protected void recomputeFloatPropertyState(final int propertyIndex,
                                          final float parentPropertyValue) {
        // We do not need to recompute the property value if:
        // - the property is _not_ inherited
        // or
        // - the property is inherited by the new parent property computed value
        //   is the same as the current value.
        if (!isInherited(propertyIndex) 
            || 
            isFloatPropertyState(propertyIndex, parentPropertyValue)) {
            // If the property is color relative, the propagation happens
            // through the color property changes.  This means that with
            // currentColor, we inherit the computed value, not the specified
            // currentColor indirection.
            return;
        }

        setFloatPropertyState(propertyIndex, parentPropertyValue);
        propagateFloatPropertyState(propertyIndex, parentPropertyValue);
    }

    /**
     * Recomputes the given packed property's state given the new parent 
     * property.
     *
     * @param propertyIndex index for the property whose value is changing.
     * @param parentPropertyValue the value that children of this node should 
     *        now inherit.
     * 
     */
    protected void recomputePackedPropertyState(final int propertyIndex,
                                                final int parentPropertyValue) {
        // We do not need to recompute the property value if:
        // - the property is _not_ inherited
        // or
        // - the property is inherited by the new parent property computed value
        //   is the same as the current value.
        if (!isInherited(propertyIndex) 
            || 
            isPackedPropertyState(propertyIndex, parentPropertyValue)) {
            // If the property is color relative, the propagation happens
            // through the color property changes.  This means that with
            // currentColor, we inherit the computed value, not the specified
            // currentColor indirection.
            return;
        }

        setPackedPropertyState(propertyIndex, parentPropertyValue);
        propagatePackedPropertyState(propertyIndex, parentPropertyValue);
    }
    
    /**
     * Called when the computed value of the given Object-valued property
     * has changed.
     *
     * @param propertyIndex index for the property whose value has changed.
     * @param parentPropertyValue the value that children of this node should 
     *        now inherit.
     * 
     */
    protected void propagatePropertyState(final int propertyIndex,
                                          final Object parentPropertyValue) {
        // Propagate to proxies.
        if (firstProxy != null) {
            ElementNodeProxy proxy = firstProxy;
            while (proxy != null) {
                ((CompositeGraphicsNodeProxy) proxy).proxiedPropertyStateChange(
                        propertyIndex, parentPropertyValue);
                proxy = proxy.nextProxy;
            }
        }

        // Propagate to regular children.
        ModelNode node = getFirstChildNode();
        while (node != null) {
            node.recomputePropertyState(propertyIndex, parentPropertyValue);
            node = node.nextSibling;
        }
    }

    /**
     * Called when the computed value of the given float-valued property has
     * changed.
     *
     * @param propertyIndex index for the property whose value has changed.
     * @param parentPropertyValue the value that children of this node should 
     *        now inherit.
     * 
     */
    protected void propagateFloatPropertyState(
            final int propertyIndex,
            final float parentPropertyValue) {
        // Propagate to proxies.
        if (firstProxy != null) {
            ElementNodeProxy proxy = firstProxy;
            while (proxy != null) {
                ((CompositeGraphicsNodeProxy) 
                        proxy).proxiedFloatPropertyStateChange(
                            propertyIndex, parentPropertyValue);
                proxy = proxy.nextProxy;
            }
        }

        // Propagate to regular children.
        ModelNode node = getFirstChildNode();
        while (node != null) {
            node.recomputeFloatPropertyState(propertyIndex, 
                                             parentPropertyValue);
            node = node.nextSibling;
        }
    }

    /**
     * Called when the computed value of the given packed property has changed.
     *
     * @param propertyIndex index for the property whose value has changed.
     * @param parentPropertyValue the value that children of this node should 
     *        now inherit.
     * 
     */
    protected void propagatePackedPropertyState(final int propertyIndex,
                                                final int parentPropertyValue) {
        // Propagate to proxies.
        if (firstProxy != null) {
            ElementNodeProxy proxy = firstProxy;
            while (proxy != null) {
                ((CompositeGraphicsNodeProxy) 
                        proxy).proxiedPackedPropertyStateChange(
                            propertyIndex, parentPropertyValue);
                proxy = proxy.nextProxy;
            }
        }

        // Propagate to regular children.
        ModelNode node = getFirstChildNode();
        while (node != null) {
            node.recomputePackedPropertyState(propertyIndex, 
                                              parentPropertyValue);
            node = node.nextSibling;
        }
    }

    /**
     * Recomputes the transform cache, if one exists. This should recursively
     * call recomputeTransformState on children node or expanded content, if
     * any.
     *
     * By default, because a ModelNode has no transform and no cached transform,
     * this only does a pass down.
     *
     * @param parentTransform the Transform applied to this node's parent.
     */
    protected void recomputeTransformState(final Transform parentTransform) {
        txf = appendTransform(parentTransform, txf);
        computeCanRenderTransformBit(txf);
        inverseTxf = null;
        // inverseTxf = computeInverseTransform(txf, parentTransform, 
        //                                      inverseTxf);
        recomputeTransformState(txf, getFirstChildNode());
    }

    /**
     * @return this node's cached transform. 
     */
    public Transform getTransformState() {
        return txf;
    }

    /**
     * @return this node's cached inverse transform. 
     */
    Transform getInverseTransformState() {
        if (((canRenderState & CAN_RENDER_NON_INVERTIBLE_TXF_BIT) == 0)) {
            if (inverseTxf == null) {
                // If there is a parent, check if this node's transform is the 
                // same as the parent's in which cahse
                if (parent != null && txf == parent.getTransformState()) {
                    inverseTxf = parent.getInverseTransformState();
                } else {
                    inverseTxf = new Transform(null);
                    try {
                        inverseTxf = (Transform) txf.inverse(inverseTxf);
                    } catch (Exception e) {
                        // If we get an exception, then we have a real error
                        // condition, because we just checked that the 
                        // transform was invertible.
                        throw new Error();
                    }
                }
            }
        } else {
            inverseTxf = null;
        }
        return inverseTxf;
    }
    
    /** 
     * Check if the property is inherited.
     *
     * @param propertyIndex the index of the property for which the 
     *        inherited state should be returned.
     * @return true if the input property is inherited. False
     * otherwise
     */
    public final boolean isInherited(final int propertyIndex) {
        return isMarkerSet(propertyIndex);
    }

    /**
     * Sets the given Object-valued property's inheritance status
     * @param propertyIndex the index for the property whose inherited state 
     *        is set
     * @param inherit the new property's state
     */
    public void setInherited(final int propertyIndex, 
                             final boolean inherit) {
        if (isInherited(propertyIndex) == inherit) {
            return;
        }
        modifyingNode();
        setInheritedQuiet(propertyIndex, inherit);

        if (inherit) {
            // The property is now inherited. We store the inherited
            // value on the node, which means we keep the computed value
            // on the node.
            Object inheritedValue = getInheritedPropertyState(propertyIndex);
            setPropertyState(propertyIndex, inheritedValue);

            // Notify children that the inherited value has changed.
            propagatePropertyState(propertyIndex, inheritedValue);
        }

        // If the value is not inherited, it means that we are in the middle of
        // specifying a value on the node. So we do not notify descendants, 
        // because this is done in the corresponding methods, e.g., setFill.

        modifiedNode();
    }

    /**
     * Sets the given float-valued property's inheritance status
     * @param propertyIndex the index for the property whose inherited state 
     *        is set
     * @param inherit the new property's state
     */
    public void setFloatInherited(final int propertyIndex, 
				  final boolean inherit) {
        if (isInherited(propertyIndex) == inherit) {
            return;
        }
        modifyingNode();
        setInheritedQuiet(propertyIndex, inherit);

        if (inherit) {
            // The property is now inherited. We store the inherited
            // value on the node, which means we keep the computed value
            // on the node.
            float inheritedValue = 
                    getInheritedFloatPropertyState(propertyIndex);
            setFloatPropertyState(propertyIndex, inheritedValue);

            // Notify children that the inherited value has changed.
            propagateFloatPropertyState(propertyIndex, inheritedValue);
        }

        // If the value is not inherited, it means that we are in the middle of
        // specifying a value on the node. So we do not notify descendants, 
        // because this is done in the corresponding methods, e.g., setFill.

        modifiedNode();
    }

    /**
     * Sets the input packed property's inheritance status
     * @param propertyIndex the index for the property whose inherited state 
     *        is set
     * @param inherit the new property's state
     */
    public void setPackedInherited(final int propertyIndex, 
                                   final boolean inherit) {
        if (isInherited(propertyIndex) == inherit) {
            return;
        }
        modifyingNode();
        setInheritedQuiet(propertyIndex, inherit);

        if (inherit) {
            // The property is now inherited. We store the inherited
            // value on the node, which means we keep the computed value
            // on the node.
            int inheritedValue = getInheritedPackedPropertyState(propertyIndex);
            setPackedPropertyState(propertyIndex, inheritedValue);

            // Notify children that the inherited value has changed.
            propagatePackedPropertyState(propertyIndex, inheritedValue);
        }

        // If the value is not inherited, it means that we are in the middle of
        // specifying a value on the node. So we do not notify descendants, 
        // because this is done in the corresponding methods, e.g., setFill.

        modifiedNode();
    }

    /**
     * Implementation. Sets the input property's inheritance status,
     * but does not send modification events.
     *
     * @param propertyIndex the index for the property whose inherited state 
     *        is set
     * @param inherit the new property's state
     */
    protected void setInheritedQuiet(final int propertyIndex, 
                                     final boolean inherit) {
        if (inherit) {
            setMarker(propertyIndex);
        } else {
            clearMarker(propertyIndex);
        }
    }

    /**
     * Returns true if the input property is color relative. False
     * otherwise
     * @param propertyIndex index for the property whose color relative
     *        state should be returned.
     * @return true if the property at index propertyIndex is relative to 
     *         the color property.
     */
    public boolean isColorRelative(final int propertyIndex) {
        return isMarkerSet(propertyIndex << 21);
    }
    
    /**
     * Returns true if the input property can be color-relative.
     * 
     * @param propertyIndex the index of the property which may be 
     *        color-relative.
     * @return true if the input property can be color relative. False 
     *         otherwise.
     */
    public boolean isColorRelativeProperty(final int propertyIndex) {
        switch (propertyIndex) {
            case PROPERTY_FILL:
                return true;
            case PROPERTY_STROKE:
                return true;
            default:
                return false;
        }
    }

    /**
     * Sets the input property as a color-relative property
     *
     * @param propertyIndex index of the property for which the color relative
     *        state is set.
     * @param isColorRelative the new color relative state for the property
     *        at propertyIndex.
     */
    public void setColorRelative(final int propertyIndex, 
                                 final boolean isColorRelative) {
        if (isColorRelative && !isColorRelativeProperty(propertyIndex)) {
            throw new IllegalArgumentException();
        }
        
        if (isColorRelative(propertyIndex) == isColorRelative) {
            return;
        }

        modifyingNode();
        setColorRelativeQuiet(propertyIndex,
                              isColorRelative);

        if (isColorRelative) {
            // The property is now color relative. We store the relative
            // value in the on the node, i.e., the computed value.
            setPropertyState(propertyIndex, color);

            // Notify children that the inherited value has changed.
            propagatePropertyState(propertyIndex, color);
        }

        modifiedNode();
    }
    
    /**
     * Sets the input marker.
     *
     * @param marker the marker to set.
     */
    void setMarker(final int marker) {
        markers |= marker;
    }
    
    /**
     * Clears the input marker.
     *
     * @param marker the marker to clear.
     */
    void clearMarker(final int marker) {
        markers &= ~marker;
    }
    
    /**
     * @return true if the input marker is set.
     */
    final boolean isMarkerSet(final int marker) {
        return (markers & marker) != 0;
    }

    /**
     * Implementation. Sets the input property as a color-relative property
     * but does not generate modification events.
     *
     * @param propertyIndex index of the property for which the color relative
     *        state is set.
     * @param isColorRelative the new color relative state for the property
     *        at propertyIndex.
     */
    protected void setColorRelativeQuiet(final int propertyIndex, 
                                         final boolean isColorRelative) {
        if (isColorRelative) {
            setMarker(propertyIndex << 21);
        } else {
            clearMarker(propertyIndex << 21);
        }
    }

    // ====================================================================
    // GraphicsNode implementation
    // ====================================================================

    /**
     * Setting the fill property clears the inherited and color relative
     * states (they are set to false).
     *
     * @param newFill the new fill property
     */
    public void setFill(final PaintServer newFill) {
        if (!isInherited(PROPERTY_FILL) && equal(newFill, fill)) {
            return;
        }

        modifyingNode();
        if (fill != null) {
            fill.dispose();
        }
        
        setComputedFill(newFill);
        setInheritedQuiet(PROPERTY_FILL, false);
        setColorRelativeQuiet(PROPERTY_FILL, false);
        propagatePropertyState(PROPERTY_FILL, fill);
        modifiedNode();
    }

    /**
     * @param newFill the new computed fill property.
     */
    void setComputedFill(final PaintServer newFill) {
        this.fill = newFill;
    }

    /**
     * @return the current fill property. This is not the computed values,
     *         i.e., it does not account for the inherited or color
     *         relative states.
     */
    public PaintServer getFill() {
        return fill;
    }

    /**
     * Setting the fillOpacity property clears the inherited and color relative
     * states (they are set to false).
     *
     * @param newFillOpacity the new fill property
     */
    public void setFillOpacity(float newFillOpacity) {
        if (!isInherited(PROPERTY_FILL_OPACITY) 
            && 
            newFillOpacity == getFillOpacity()) {
            return;
        }
        modifyingNode();
        if (newFillOpacity > 1) {
            newFillOpacity = 1;
        } else if (newFillOpacity < 0) {
            newFillOpacity = 0;
        }
        setInheritedQuiet(PROPERTY_FILL_OPACITY, false);
        setComputedFillOpacity(newFillOpacity);
        propagatePackedPropertyState(PROPERTY_FILL_OPACITY, 
                                     pack & FILL_OPACITY_MASK);
        modifiedNode();
    }

    /**
     * @param newFillOpacity the new computed value of the fill opacity 
     *        property.
     */
    void setComputedFillOpacity(final float newFillOpacity) {                
        pack &= ~FILL_OPACITY_MASK;
        pack |= ((((int) (newFillOpacity * 200)) << 7) & FILL_OPACITY_MASK);
    }

    /**
     * @return the current fillOpacity property. 
     */
    public float getFillOpacity() {
        return ((pack & FILL_OPACITY_MASK) >> 7) / 200.0f;
    }

    /**
     * Setting the stroke clears the inherited and color relative states
     * (i.e., they are set to false).
     *
     * @param newStroke new stroke property. 
     */
    public void setStroke(final PaintServer newStroke) {
        if (!isInherited(PROPERTY_STROKE) && equal(newStroke, stroke)) {
            return;
        }

        modifyingNode();
        if (newStroke != stroke) {
            if (stroke != null) {
                stroke.dispose();
            }
        }
        setInheritedQuiet(PROPERTY_STROKE, false);
        setColorRelativeQuiet(PROPERTY_STROKE, false);
        setComputedStroke(newStroke);
        propagatePropertyState(PROPERTY_STROKE, stroke);
        modifiedNode();
    }

    /**
     * @param newStroke the new computed stroke property.
     */
    void setComputedStroke(final PaintServer newStroke) {
        this.stroke = newStroke;
    }

    /**
     * @return the stroke property. This is not the computed value as
     *         it does not account for the inherited and color relative
     *         states.
     */
    public PaintServer getStroke() {
        return stroke;
    }

    /**
     * Setting the strokeOpacity property clears the inherited and color 
     * relative states (they are set to false).
     *
     * @param newStrokeOpacity the new stroke property
     */
    public void setStrokeOpacity(float newStrokeOpacity) {
        if (!isInherited(PROPERTY_STROKE_OPACITY) && 
            newStrokeOpacity == getStrokeOpacity()) {
            return;
        }

        modifyingNode();
        if (newStrokeOpacity > 1) {
            newStrokeOpacity = 1;
        } else if (newStrokeOpacity < 0) {
            newStrokeOpacity = 0;
        }
        setInheritedQuiet(PROPERTY_STROKE_OPACITY, false);
        setComputedStrokeOpacity(newStrokeOpacity);
        propagatePackedPropertyState(PROPERTY_STROKE_OPACITY, 
                                     pack & STROKE_OPACITY_MASK);
        modifiedNode();
    }

    /**
     * @param newStrokeOpacity the new computed stroke-opacity property.
     */
    void setComputedStrokeOpacity(final float newStrokeOpacity) {
        pack &= ~STROKE_OPACITY_MASK;
        pack |= ((((int) (newStrokeOpacity * 200)) << 15) 
                & STROKE_OPACITY_MASK);
    }

    /**
     * @return the current strokeOpacity property. 
     */
    public float getStrokeOpacity() {
        return ((pack & STROKE_OPACITY_MASK) >> 15) / 200.0f;
    }

    /**
     * Setting the color property clears this property's inherited flag.
     * @param newColor new color property.
     */
    public void setColor(final RGB newColor) {
        if (!isInherited(PROPERTY_COLOR) && equal(newColor, color)) {
            return;
        }
        modifyingNode();
        setComputedColor(newColor);
        setInheritedQuiet(PROPERTY_COLOR, false);
        propagatePropertyState(PROPERTY_COLOR, color);
        modifiedNode();
    }

    /**
     * @param newColor the new computed color property.
     */
    void setComputedColor(final RGB newColor) {
        color = newColor;

        // We need to recompute the fill and stroke colors if they are 
        // color-relative.
        if (isColorRelative(PROPERTY_FILL)) {
            setComputedFill(newColor);
            propagatePropertyState(PROPERTY_FILL, fill);
        }

        if (isColorRelative(PROPERTY_STROKE)) {
            setComputedStroke(newColor);
            propagatePropertyState(PROPERTY_STROKE, stroke);
        }
    }

    /**
     * @return the current color property. This is not the computed
     *         value and does not account for the inherited state.
     */
    public RGB getColor() {
        return color;
    }

    /**
     * Setting the fillRule property clears its inherited flag
     * @param newFillRule new fillRule property
     */
    public void setFillRule(final int newFillRule) {
        if (!isInherited(PROPERTY_FILL_RULE) 
            && 
            newFillRule == getFillRule()) {
            return;
        }

        modifyingNode();
        setInheritedQuiet(PROPERTY_FILL_RULE, false);
        setComputedFillRule(newFillRule);
        propagatePackedPropertyState(PROPERTY_FILL_RULE, pack & FILL_RULE_MASK);
        modifiedNode();
    }

    /**
     * @param newFillRule the new computed fillRule property value.
     */
    final void setComputedFillRule(final int newFillRule) {
        if (newFillRule == WIND_NON_ZERO) {
            pack |= FILL_RULE_MASK;
        } else {
            pack &= ~FILL_RULE_MASK;
        }
    }

    /**
     * @return the current fillRule property, exclusive of
     *         the inherited flag.
     */
    public int getFillRule() {
        if ((pack & FILL_RULE_MASK) == FILL_RULE_MASK) {
            return WIND_NON_ZERO;
        }
        return WIND_EVEN_ODD;
    }

    /**
     * Setting the strokeDashArray property clears its inherited flag.
     *
     * @param newStrokeDashArray new strokeDashArray property.
     */
    public void setStrokeDashArray(final float[] newStrokeDashArray) {
        if (!isInherited(PROPERTY_STROKE_DASH_ARRAY) 
                && equal(newStrokeDashArray, strokeDashArray)) {
            return;
        }
        modifyingNode();
        setComputedStrokeDashArray(newStrokeDashArray);
        setInheritedQuiet(PROPERTY_STROKE_DASH_ARRAY, false);
        propagatePropertyState(PROPERTY_STROKE_DASH_ARRAY, newStrokeDashArray);
        modifiedNode();
    }

    /**
     * @param newStrokeDashArray the new computed stroke-dasharray property 
     *        value.
     */
    void setComputedStrokeDashArray(final float[] newStrokeDashArray) {
        strokeDashArray = newStrokeDashArray;
    }

    /**
     * @return current strokeDashArray, exclusive of the inherited 
     *         state.
     */
    public float[] getStrokeDashArray() {
        return strokeDashArray;
    }

    /**
     * Setting the strokeLineCap property clears the inherited flag
     *
     * @param newStrokeLineCap new strokeLineCap property
     */
    public void setStrokeLineCap(final int newStrokeLineCap) {
        if (!isInherited(PROPERTY_STROKE_LINE_CAP) 
            && newStrokeLineCap == getStrokeLineCap()) {
            return;
        }
        modifyingNode();        
        setInheritedQuiet(PROPERTY_STROKE_LINE_CAP, false);
        setComputedStrokeLineCap(newStrokeLineCap);
        propagatePackedPropertyState(PROPERTY_STROKE_LINE_CAP, 
                                     pack & STROKE_LINE_CAP_MASK);
        modifiedNode();
    }

    /**
     * @param newStrokeLineCap the new value for the stroke-linecap property.
     */
    void setComputedStrokeLineCap(final int newStrokeLineCap) {
        // Clear stroke-linecap
        pack &= ~STROKE_LINE_CAP_MASK;

        switch (newStrokeLineCap) {
        case CAP_BUTT:
            pack |= CAP_BUTT_IMPL;
            break;
        case CAP_ROUND:
            pack |= CAP_ROUND_IMPL;
            break;
        default:
            pack |= CAP_SQUARE_IMPL;
            break;
        }
    }

    /**
     * @return the strokeLineCap property exclusive of its inherited state
     */
    public int getStrokeLineCap() {
        switch (pack & STROKE_LINE_CAP_MASK) {
        case CAP_BUTT_IMPL:
            return CAP_BUTT;
        case CAP_ROUND_IMPL:
            return CAP_ROUND;
        default:
            return CAP_SQUARE;
        }
    }

    /**
     * Setting the strokeLineJoin property clears the inherited flag
     *
     * @param newStrokeLineJoin new strokeLineJoin property
     */
    public void setStrokeLineJoin(final int newStrokeLineJoin) {
        if (!isInherited(PROPERTY_STROKE_LINE_JOIN) 
            && newStrokeLineJoin == getStrokeLineJoin()) {
            return;
        }
        modifyingNode();
        setInheritedQuiet(PROPERTY_STROKE_LINE_JOIN, false);
        setComputedStrokeLineJoin(newStrokeLineJoin);
        propagatePackedPropertyState(PROPERTY_STROKE_LINE_JOIN, 
                                     pack & STROKE_LINE_JOIN_MASK);
        modifiedNode();
    }

    /**
     * @param newStrokeLineJoin the new computed value of stroke-line-join
     */
    void setComputedStrokeLineJoin(final int newStrokeLineJoin) {
        // Clear stroke-linejoin
        pack &= ~STROKE_LINE_JOIN_MASK;

        switch (newStrokeLineJoin) {
        case JOIN_MITER:
            pack |= JOIN_MITER_IMPL;
            break;
        case JOIN_ROUND:
            pack |= JOIN_ROUND_IMPL;
            break;
        default:
            pack |= JOIN_BEVEL_IMPL;
            break;
        }
    }

    /**
     * @return current strokeLineJoin exclusive of the inherited state
     */
    public int getStrokeLineJoin() {
        switch (pack & STROKE_LINE_JOIN_MASK) {
        case JOIN_MITER_IMPL:
            return JOIN_MITER;
        case JOIN_ROUND_IMPL:
            return JOIN_ROUND;
        default:
            return JOIN_BEVEL;
        }
    }

    /**
     * Setting the strokeWidth property clears its inherited flag.
     *
     * @param newStrokeWidth new strokeWidth property. Should be 
     *        positive or zero.
     */
    public void setStrokeWidth(final float newStrokeWidth) {
        if (newStrokeWidth < 0) {
            throw new IllegalArgumentException();
        }

        if (!isInherited(PROPERTY_STROKE_WIDTH) 
                && newStrokeWidth == strokeWidth) {
            return;
        }

        modifyingNode();
        setInheritedQuiet(PROPERTY_STROKE_WIDTH, false);
        setComputedStrokeWidth(newStrokeWidth);
        propagateFloatPropertyState(PROPERTY_STROKE_WIDTH, newStrokeWidth);
        modifiedNode();
    }

    /**
     * @param newStrokeWidth the new computed stroke-width property value.
     */
    void setComputedStrokeWidth(final float newStrokeWidth) {
        strokeWidth = newStrokeWidth;
    }

    /**
     * @return current strokeWidth, exclusive of inheritance
     */
    public float getStrokeWidth() {
        return strokeWidth;
    }

    /**
     * Setting the strokeMiterLimit clears its inherited flag.
     *
     * @param newStrokeMiterLimit new strokeMiterLimit property
     */
    public void setStrokeMiterLimit(final float newStrokeMiterLimit) {
        if (newStrokeMiterLimit < 1) {
            throw new IllegalArgumentException();
        }

        if (!isInherited(PROPERTY_STROKE_MITER_LIMIT) 
                && newStrokeMiterLimit == strokeMiterLimit) {
            return;
        }

        modifyingNode();
        setComputedStrokeMiterLimit(newStrokeMiterLimit);
        setInheritedQuiet(PROPERTY_STROKE_MITER_LIMIT, false);
        propagateFloatPropertyState(PROPERTY_STROKE_MITER_LIMIT, 
                                    strokeMiterLimit);
        modifiedNode();
    }

    /**
     * @param newStrokeMiterLimit the new computed stroke-miterlimit property.
     */
    void setComputedStrokeMiterLimit(final float newStrokeMiterLimit) {
        strokeMiterLimit = newStrokeMiterLimit;        
    }

    /**
     * @return current strokeMiterLimit, exclusive of the inherited state
     */
    public float getStrokeMiterLimit() {
        return strokeMiterLimit;
    }

    /**
     * Setting the strokeDashOffset property clears its inherited flag.
     *
     * @param newStrokeDashOffset new strokeDashOffset value
     */
    public void setStrokeDashOffset(final float newStrokeDashOffset) {
        if (!isInherited(PROPERTY_STROKE_DASH_OFFSET) 
                && newStrokeDashOffset == strokeDashOffset) {
            return;
        }

        modifyingNode();
        setComputedStrokeDashOffset(newStrokeDashOffset);
        setInheritedQuiet(PROPERTY_STROKE_DASH_OFFSET, false);
        propagateFloatPropertyState(PROPERTY_STROKE_DASH_OFFSET, 
                                    strokeDashOffset);
        modifiedNode();
    }

    /**
     * @param newStrokeDashOffset the new stroke-dashoffset computed property 
     *        value.
     */
    void setComputedStrokeDashOffset(final float newStrokeDashOffset) {
        strokeDashOffset = newStrokeDashOffset;
    }

    /**
     * @return current strokeDashOffset property value, exclusive of its
     *         inherited state.
     */
    public float getStrokeDashOffset() {
        return strokeDashOffset;
    }

    /**
     * Setting the visibility clears its inherited flag.
     *
     * @param newVisibility the new visibility value
     */
    public void setVisibility(final boolean newVisibility) {
        if (!isInherited(PROPERTY_VISIBILITY) 
            && newVisibility == getVisibility()) {
            return;
        }
        modifyingNode();
        setComputedVisibility(newVisibility);
        setInheritedQuiet(PROPERTY_VISIBILITY, false);
        propagatePackedPropertyState(PROPERTY_VISIBILITY, 
                                     pack & VISIBILITY_MASK);
        modifiedNode();
    }

    /**
     * @param newVisibility the new computed visibility property.
     */
    void setComputedVisibility(final boolean newVisibility) {
        if (newVisibility) {
            pack |= VISIBILITY_MASK;
        } else {
            pack &= ~VISIBILITY_MASK;
        }        
    }

    /**
     * @return current visibility property, exclusive of inheritance.
     */
    public boolean getVisibility() {
        return ((pack & VISIBILITY_MASK) == VISIBILITY_MASK);
    }

    /**
     * Setting the display property clears its inherited flag.
     * 
     * @param newDisplay new display property value
     */
    public void setDisplay(final boolean newDisplay) {
        if (!isInherited(PROPERTY_DISPLAY) 
            && newDisplay == getDisplay()) {
            return;
        }
        modifyingNode();
        setInheritedQuiet(PROPERTY_DISPLAY, false);
        setComputedDisplay(newDisplay);
        propagatePackedPropertyState(PROPERTY_DISPLAY, pack & DISPLAY_MASK);
        modifiedNode();
    }

    /**
     * @param newDisplay the new computed display value
     */
    void setComputedDisplay(final boolean newDisplay) {
        if (newDisplay) {
            pack |= DISPLAY_MASK;
        } else {
            pack &= ~DISPLAY_MASK;
        }

        computeCanRenderDisplayBit(newDisplay);
    }

    /**
     * @return current display property value, exclusive of inheritance
     */
    public boolean getDisplay() {
        return ((pack & DISPLAY_MASK) == DISPLAY_MASK);
    }

    /**
     * Setting the opacity property clears the inherited and color 
     * relative states (they are set to false).
     *
     * @param newOpacity the new opacity property
     */
    public void setOpacity(float newOpacity) {

        if (!isInherited(PROPERTY_OPACITY) && newOpacity == getOpacity()) {
            return;
        }

        modifyingNode();
        if (newOpacity > 1) {
            newOpacity = 1;
        } else if (newOpacity < 0) {
            newOpacity = 0;
        }

        setInheritedQuiet(PROPERTY_OPACITY, false);
        setComputedOpacity(newOpacity);
        propagatePackedPropertyState(PROPERTY_OPACITY, 
                                     pack2 & OPACITY_MASK);
        modifiedNode();
    }

    /**
     * @param newOpacity the new computed opacity property.
     */
    void setComputedOpacity(final float newOpacity) {
        pack2 &= ~OPACITY_MASK;
        pack2 |= (((int) (newOpacity * 200)) & OPACITY_MASK);
    }

    /**
     * @return the current opacity property value. 
     */
    public float getOpacity() {
        return (pack2 & OPACITY_MASK) / 200.0f;
    }

    /**
     * Supported traits: stroke-width, stroke-miterlimit, stroke-dashoffset,
     * fill-rule, stroke-linejoin, stroke-linecap, display, visibility, 
     * color, fill, stroke, fill-opacity, stroke-opacity, stroke-dasharray,
     * opacity
     *
     * @param traitName the name of the trait which the element may support.
     * @return true if this element supports the given trait in one of the
     *         trait accessor methods.
     */
    boolean supportsTrait(final String traitName) {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FILL_RULE_ATTRIBUTE == traitName
            || 
            SVGConstants.SVG_STROKE_LINEJOIN_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_LINECAP_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_DISPLAY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_VISIBILITY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_COLOR_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FILL_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_OPACITY_ATTRIBUTE == traitName)  {
            return true;
        } else {
            return super.supportsTrait(traitName);
        }
    }

    /**
     * Returns the specified trait value as String. In SVG Tiny only certain
     * traits can be obtained as a String value. Syntax of the returned String
     * matches the syntax of the corresponding attribute. This element is
     * exactly equivalent to {@link org.w3c.dom.svg.SVGElement#getTraitNS
     * getTraitNS} with namespaceURI set to null.
     *
     * The method is meant to be overridden by derived classes. The 
     * implementation pattern is that derived classes will override the method 
     * and call their super class' implementation. If the ElementNode 
     * implementation is called, it means that the trait is either not supported
     * or that it cannot be seen as a String.
     *
     * @param name the requested trait name.
     * @return the trait value.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
     * trait's computed value cannot be converted to a String (SVG Tiny only).
     */
    String getSpecifiedTraitImpl(final String name) throws DOMException {
        if ((SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE_WIDTH))
            ||
            (SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE_MITER_LIMIT))
            ||
            (SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE_DASH_OFFSET))
            ||
            (SVGConstants.SVG_FILL_RULE_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_FILL_RULE))
            ||
            (SVGConstants.SVG_STROKE_LINEJOIN_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE_LINE_JOIN))
            ||
            (SVGConstants.SVG_STROKE_LINECAP_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE_LINE_CAP))
            ||
            (SVGConstants.SVG_DISPLAY_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_DISPLAY))
            ||
            (SVGConstants.SVG_VISIBILITY_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_VISIBILITY))
            ||
            (SVGConstants.SVG_COLOR_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_COLOR))
            ||
            (SVGConstants.SVG_FILL_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_FILL))
            ||
            (SVGConstants.SVG_STROKE_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE))
            ||
            (SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_FILL_OPACITY))
            ||
            (SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE_OPACITY))
            ||
            (SVGConstants.SVG_OPACITY_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_OPACITY))
            ||
            (SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE == name
             &&
             isInherited(PROPERTY_STROKE_DASH_ARRAY))) {
            return SVGConstants.CSS_INHERIT_VALUE;
        } else if ((SVGConstants.SVG_FILL_ATTRIBUTE == name
                    &&
                    isColorRelative(PROPERTY_FILL)) 
                   ||
                   (SVGConstants.SVG_STROKE_ATTRIBUTE == name
                    &&
                    isColorRelative(PROPERTY_STROKE))) {
            return SVGConstants.CSS_CURRENTCOLOR_VALUE;
        } else if (SVGConstants.SVG_DISPLAY_ATTRIBUTE == name) {
            if (getDisplay()) {
                return SVGConstants.CSS_INLINE_VALUE;
            } else {
                return SVGConstants.CSS_NONE_VALUE;
            }
        } else {
            return super.getSpecifiedTraitImpl(name);
        }
    }

    /**
     * Converts the input fill-rule value to a string trait value.
     *
     * @param fillRule the fill-rule value to convert. In packed form, but
     *        with mask applied.
     * @return the converted value.
     */
    String fillRuleToStringTrait(final int fillRule) {
        if (fillRule == CompositeGraphicsNode.FILL_RULE_MASK) {
            return SVGConstants.CSS_NONZERO_VALUE;
        }
        return SVGConstants.CSS_EVENODD_VALUE;
    }

    /**
     * @param strokeLineJoin the value to convert. In packed form, but with
     *        mask applied.
     * @return the converted value.
     */
    String strokeLineJoinToStringTrait(final int strokeLineJoin) {
        switch (strokeLineJoin) {
        case CompositeGraphicsNode.JOIN_MITER_IMPL:
            return SVGConstants.CSS_MITER_VALUE;
        case CompositeGraphicsNode.JOIN_ROUND_IMPL:
            return SVGConstants.CSS_ROUND_VALUE;
        default:
            return SVGConstants.CSS_BEVEL_VALUE;
        }
    }

    /**
     * @param strokeLineCap the value to convert.
     * @return the converted value.
     */
    String strokeLineCapToStringTrait(final int strokeLineCap) {
        switch (strokeLineCap) {
        case CompositeGraphicsNode.CAP_BUTT_IMPL:
            return SVGConstants.CSS_BUTT_VALUE;
        case CompositeGraphicsNode.CAP_ROUND_IMPL:
            return SVGConstants.CSS_ROUND_VALUE;
        case CompositeGraphicsNode.CAP_SQUARE_IMPL:
        default:
            return SVGConstants.CSS_SQUARE_VALUE;
        }
    }

    /**
     * Supported traits: stroke-width, stroke-miterlimit, stroke-dashoffset,
     * fill-rule, stroke-linejoin, stroke-linecap, display, visibility, 
     * color, fill, stroke, fill-opacity, stroke-opacity, stroke-dasharray,
     * opacity
     *
     * @param name the requested trait's name
     * @return the requested trait value, as a string.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
     * trait's computed value cannot be converted to a String (SVG Tiny only).
     */
    public String getTraitImpl(final String name)
        throws DOMException {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == name) {
            return Float.toString(getStrokeWidth());
        } else if (SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == name) {
            return Float.toString
                (getStrokeMiterLimit());
        } else if (SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == name) {
            return Float.toString
                (getStrokeDashOffset());
        } else if (SVGConstants.SVG_FILL_RULE_ATTRIBUTE == name) { 
            return fillRuleToStringTrait(pack & FILL_RULE_MASK);
        } else if (SVGConstants.SVG_STROKE_LINEJOIN_ATTRIBUTE == name) {
            return strokeLineJoinToStringTrait
                (pack & STROKE_LINE_JOIN_MASK);
        } else if (SVGConstants.SVG_STROKE_LINECAP_ATTRIBUTE == name) {
            return strokeLineCapToStringTrait
                (pack & STROKE_LINE_CAP_MASK);
        } else if (SVGConstants.SVG_DISPLAY_ATTRIBUTE == name) {
            if (getDisplay()) {
                return SVGConstants.CSS_INLINE_VALUE;
            } else {
                return SVGConstants.CSS_NONE_VALUE;
            }
        } else if (SVGConstants.SVG_VISIBILITY_ATTRIBUTE == name) {
            if (getVisibility()) {
                return SVGConstants.CSS_VISIBLE_VALUE;
            } else {
                return SVGConstants.CSS_HIDDEN_VALUE;
            }
        } else if (SVGConstants.SVG_COLOR_ATTRIBUTE == name) {
            return getColor().toString();
        } else if (SVGConstants.SVG_FILL_ATTRIBUTE == name) {
            return toString(getFill());
        } else if (SVGConstants.SVG_STROKE_ATTRIBUTE == name) {
            return toString(getStroke());
        } else if (SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == name) {
            return Float.toString(getFillOpacity());
        } else if (SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == name) {
            return Float.toString(getStrokeOpacity());
        } else if (SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE == name) {
            return toStringTrait(getStrokeDashArray());
        } else if (SVGConstants.SVG_OPACITY_ATTRIBUTE == name) {
            return Float.toString(getOpacity());
        } else {
            return super.getTraitImpl(name);
        }
    }

    /**
     * Supported float traits: stroke-width, stroke-miterlimit, 
     * stroke-dashoffset, fill-opacity, stroke-opacity, opacity.
     *
     * @param name the requested trait's name
     * @return the requested trait's value, as a floating point.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
     * trait's computed value cannot be converted to a float
     * @throws SecurityException if the application does not have the necessary
     * privilege rights to access this (SVG) content.
     */
    float getFloatTraitImpl(final String name)
        throws DOMException {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == name) {
            return getStrokeWidth();
        } else if (SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == name) {
            return getStrokeMiterLimit();
        } else if (SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == name) {
            return getStrokeDashOffset();
        } else if (SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == name) {
            return getFillOpacity();
        } else if (SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == name) {
            return getStrokeOpacity();
        } else if (SVGConstants.SVG_OPACITY_ATTRIBUTE == name) {
            return getOpacity();
        } else {
            return super.getFloatTraitImpl(name);
        }
    }

    /**
     * Supported color traits: color, fill, stroke
     *
     * @param name the requested trait's name.
     * @return the requested trait's value, as an <code>SVGRGBColor</code>.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
     * trait's computed value cannot be converted to {@link
     * org.w3c.dom.svg.SVGRGBColor SVGRGBColor}
     * @throws SecurityException if the application does not have the necessary
     * privilege rights to access this (SVG) content.
     */
    SVGRGBColor getRGBColorTraitImpl(String name)
        throws DOMException {
        if (SVGConstants.SVG_FILL_ATTRIBUTE.equals(name)) {
            return toSVGRGBColor(SVGConstants.SVG_FILL_ATTRIBUTE,
                                 getFill());
        } else if (SVGConstants.SVG_STROKE_ATTRIBUTE.equals(name)) {
            return toSVGRGBColor(SVGConstants.SVG_STROKE_ATTRIBUTE,
                                 getStroke());
        } else if (SVGConstants.SVG_COLOR_ATTRIBUTE.equals(name)) {
            return toSVGRGBColor(SVGConstants.SVG_COLOR_ATTRIBUTE,
                                 getColor());
        } else {
            return super.getRGBColorTraitImpl(name);
        }
    }

    /**
     * @param traitName the trait name.
     */
    TraitAnim createTraitAnimImpl(final String traitName) {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_OPACITY_ATTRIBUTE == traitName) {
            return new FloatTraitAnim(this, traitName, TRAIT_TYPE_FLOAT);
        } else if (SVGConstants.SVG_COLOR_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_FILL_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_STROKE_ATTRIBUTE == traitName) {
            return new FloatTraitAnim(this, traitName, 
                                      TRAIT_TYPE_SVG_RGB_COLOR);
        } else if (SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE == traitName) {
            return new FloatTraitAnim(this, traitName, TRAIT_TYPE_STRING);
        } else if (SVGConstants.SVG_DISPLAY_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_FILL_RULE_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_STROKE_LINECAP_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_STROKE_LINEJOIN_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_VISIBILITY_ATTRIBUTE == traitName) {
            return new StringTraitAnim(this, NULL_NS, traitName);
        } else {
            return super.createTraitAnimImpl(traitName);
        }
    }

    /**
     * Set the trait value as float array.
     *
     * @param name the trait's name.
     * @param value the trait's value.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
     * trait's value cannot be specified as a float
     * @throws DOMException with error code INVALID_ACCESS_ERR if the input
     * value is an invalid value for the given trait.
     */
    void setFloatArrayTrait(final String name, final float[][] value)
        throws DOMException {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == name) {
            checkPositive(name, value[0][0]);
            setStrokeWidth(value[0][0]);
        } else if (SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE
                   .equals(name)) {
            if (value[0][0] < 1) {
                throw illegalTraitValue(name, Float.toString(value[0][0]));
            }

            setStrokeMiterLimit(value[0][0]);
        } else if (SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE
                   .equals(name)) {
            setStrokeDashOffset(value[0][0]);
        } else if (SVGConstants.SVG_COLOR_ATTRIBUTE == name) {
            setColor(toRGB(name, value));
        } else if (SVGConstants.SVG_FILL_ATTRIBUTE == name) {
            setFill(toRGB(name, value));
        } else if (SVGConstants.SVG_STROKE_ATTRIBUTE == name) {
            setStroke(toRGB(name, value));
        } else if (SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == name) {
            setFillOpacity(value[0][0]);
        } else if (SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == name) {
            setStrokeOpacity(value[0][0]);
        } else if (SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE
                   .equals(name)) {
            setStrokeDashArray(value[0]);
        } else if (SVGConstants.SVG_OPACITY_ATTRIBUTE == name) {
            setOpacity(value[0][0]);
         } else {
            super.setFloatArrayTrait(name, value);
        }    
    }
    
    /**
     * Validates the input trait value.
     *
     * @param namespaceURI the trait's namespace URI.
     * @param traitName the name of the trait to be validated.
     * @param value the value to be validated
     * @param reqNamespaceURI the namespace of the element requesting 
     *        validation.
     * @param reqLocalName the local name of the element requesting validation.
     * @param reqTraitNamespace the namespace of the trait which has the values
     *        value on the requesting element.
     * @param reqTraitName the name of the trait which has the values value on 
     *        the requesting element.
     * @throws DOMException with error code INVALID_ACCESS_ERR if the input
     * value is incompatible with the given trait.
     */
    String validateTraitNS(final String namespaceURI,
                           final String traitName,
                           final String value,
                           final String reqNamespaceURI,
                           final String reqLocalName,
                           final String reqTraitNamespace,
                           final String reqTraitName) throws DOMException {
        if (namespaceURI != null && namespaceURI != NULL_NS) {
            return super.validateTraitNS(namespaceURI,
                                         traitName,
                                         value,
                                         reqNamespaceURI,
                                         reqLocalName,
                                         reqTraitNamespace,
                                         reqTraitName);
        }

        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_COLOR_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FILL_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE
            .equals(traitName)
            ||
            SVGConstants.SVG_OPACITY_ATTRIBUTE == traitName) {
            throw unsupportedTraitType(traitName, TRAIT_TYPE_FLOAT);
        } else if (SVGConstants.SVG_FILL_RULE_ATTRIBUTE == traitName) {
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                return fillRuleToStringTrait
                        (getInheritedPackedPropertyState(PROPERTY_FILL_RULE));
            }

            if (!SVGConstants.CSS_NONZERO_VALUE.equals(value)
                &&
                !SVGConstants.CSS_EVENODD_VALUE.equals(value)) {
                throw illegalTraitValue(traitName, value);
            }

            return value;
        } else if (SVGConstants.SVG_STROKE_LINEJOIN_ATTRIBUTE == traitName) {
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                return strokeLineJoinToStringTrait(
                        getInheritedPackedPropertyState(
                            PROPERTY_STROKE_LINE_JOIN));
            }

            if (!SVGConstants.CSS_MITER_VALUE.equals(value)
                &&
                !SVGConstants.CSS_ROUND_VALUE.equals(value)
                &&
                !SVGConstants.CSS_BEVEL_VALUE.equals(value)) {
                throw illegalTraitValue(traitName, value);
            }

            return value;
        } else if (SVGConstants.SVG_STROKE_LINECAP_ATTRIBUTE == traitName) {
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                return strokeLineCapToStringTrait(
                        getInheritedPackedPropertyState(
                            PROPERTY_STROKE_LINE_CAP));
            }

            if (!SVGConstants.CSS_BUTT_VALUE.equals(value)
                &&
                !SVGConstants.CSS_ROUND_VALUE.equals(value)
                &&
                !SVGConstants.CSS_SQUARE_VALUE.equals(value)) {
                throw illegalTraitValue(traitName, value);
            }

            return value;
        } else if (SVGConstants.SVG_DISPLAY_ATTRIBUTE == traitName) {
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                if (getInheritedPackedPropertyState(PROPERTY_DISPLAY) != 0) {
                    return SVGConstants.CSS_INLINE_VALUE;
                }
                return SVGConstants.CSS_NONE_VALUE;
            }

            if (!SVGConstants.CSS_INLINE_VALUE.equals(value)
                &&
                !SVGConstants.CSS_NONE_VALUE.equals(value)) {
                throw illegalTraitValue(traitName, value);
            }

            return value;
        } else if (SVGConstants.SVG_VISIBILITY_ATTRIBUTE == traitName) {
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                if (getInheritedPackedPropertyState(PROPERTY_VISIBILITY) != 0) {
                    return SVGConstants.CSS_VISIBLE_VALUE;
                } else {
                    return SVGConstants.CSS_HIDDEN_VALUE;
                }
            }

            if (!SVGConstants.CSS_VISIBLE_VALUE.equals(value)
                &&
                !SVGConstants.CSS_HIDDEN_VALUE.equals(value)) {
                throw illegalTraitValue(traitName, value);
            }

            return value;
        } 

        return super.validateTraitNS(namespaceURI,
                                     traitName,
                                     value,
                                     reqNamespaceURI,
                                     reqLocalName,
                                     reqTraitNamespace,
                                     reqTraitName);
    }
 
    /**
     * Validates the float input trait value.
     *
     * @param traitName the name of the trait to be validated.
     * @param value the value to be validated
     * @param reqNamespaceURI the namespace of the element requesting 
     *        validation.
     * @param reqLocalName the local name of the element requesting validation.
     * @param reqTraitNamespace the namespace of the trait which has the values
     *        value on the requesting element.
     * @param reqTraitName the name of the trait which has the values value on 
     *        the requesting element.
     * @throws DOMException with error code INVALID_ACCESS_ERR if the input
     * value is incompatible with the given trait.
     */
    public float[][] validateFloatArrayTrait(
            final String traitName,
            final String value,
            final String reqNamespaceURI,
            final String reqLocalName,
            final String reqTraitNamespace,
            final String reqTraitName) throws DOMException {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == traitName) {
            return new float[][] { 
                        {parsePositiveFloatTrait(traitName, value)} 
                    };
        } else if (SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE
                   .equals(traitName)) {
            float miter = parseFloatTrait(traitName, value);
            if (miter < 1) {
                throw illegalTraitValue(traitName, value);
            }
            return new float[][] { {miter} };
        } else if (SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE
                   .equals(traitName)) {
            return new float[][] { {parseFloatTrait(traitName, value)} };
        } else if (SVGConstants.SVG_FILL_RULE_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_STROKE_LINEJOIN_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_STROKE_LINECAP_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_DISPLAY_ATTRIBUTE == traitName
                   ||
                   SVGConstants.SVG_VISIBILITY_ATTRIBUTE == traitName)  {
            throw unsupportedTraitType(traitName, TRAIT_TYPE_FLOAT);
        } else if (SVGConstants.SVG_COLOR_ATTRIBUTE == traitName) {
            RGB color = GraphicsProperties.INITIAL_COLOR;
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                color = (RGB) getInheritedPropertyState(PROPERTY_COLOR);
            } else {
                color = parseColorTrait
                    (SVGConstants.SVG_COLOR_ATTRIBUTE, value);
            }

            if (color == null) {
                throw illegalTraitValue(traitName, value);
            }
            return new float[][] {
                        {color.getRed(), color.getGreen(), color.getBlue()}
                    };
        } else if (SVGConstants.SVG_FILL_ATTRIBUTE == traitName) {
            RGB color = GraphicsProperties.INITIAL_FILL;
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                color = (RGB) getInheritedPropertyState(PROPERTY_FILL);
            } else if (SVGConstants.CSS_CURRENTCOLOR_VALUE.equals(value)) {
                color = this.color;
            } else {
                color = parseColorTrait
                    (SVGConstants.SVG_FILL_ATTRIBUTE, value);
            }

            if (color == null) {
                throw illegalTraitValue(traitName, value);
            }
            return new float[][] {
                        {color.getRed(), color.getGreen(), color.getBlue()}
                    };
        } else if (SVGConstants.SVG_STROKE_ATTRIBUTE == traitName) {
            RGB color = GraphicsProperties.INITIAL_STROKE;
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                color = (RGB) getInheritedPropertyState(PROPERTY_STROKE);
            } else if (SVGConstants.CSS_CURRENTCOLOR_VALUE.equals(value)) {
                color = getColor();
            } else {
                color = parseColorTrait
                    (SVGConstants.SVG_STROKE_ATTRIBUTE, value);
            }

            if (color == null) {
                throw illegalTraitValue(traitName, value);
            }
            return new float[][] {
                        {color.getRed(), color.getGreen(), color.getBlue()}
                    };
        } else if (SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == traitName) {
            float v = GraphicsNode.INITIAL_FILL_OPACITY;
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                if (parent != null) {
                    v = (getInheritedPackedPropertyState(PROPERTY_FILL_OPACITY)
                            >> 7) / 200.0f;
                }
            } else {
                v = parseFloatTrait(traitName, value);
                if (v < 0) {
                    v = 0;
                } else if (v > 1) {
                    v = 1;
                }
            }
            return new float[][] {{v}};
        } else if (SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == traitName) {
            float v = GraphicsProperties.INITIAL_STROKE_OPACITY;
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                v = (getInheritedPackedPropertyState(PROPERTY_STROKE_OPACITY) 
                        >> 15) / 200.0f;
            } else {
                v = parseFloatTrait(traitName, value);
                if (v < 0) {
                    v = 0;
                } else if (v > 1) {
                    v = 1;
                }
            }
            return new float[][] {{v}};
        } else if (SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE
                   .equals(traitName)) {
            float[] da = parsePositiveFloatArrayTrait(traitName, value);
            if (da == null) {
                throw illegalTraitValue(traitName, value);
            } else {
                return new float[][] {da};
            }
        } else if (SVGConstants.SVG_OPACITY_ATTRIBUTE == traitName) {
            float v = GraphicsProperties.INITIAL_OPACITY;
            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                v = getInheritedPackedPropertyState(PROPERTY_OPACITY) / 
                        200.0f;
            } else {
                v = parseFloatTrait(traitName, value);
                if (v < 0) {
                    v = 0;
                } else if (v > 1) {
                    v = 1;
                }
            }
            return new float[][] {{v}};
        } else {
            return super.validateFloatArrayTrait(traitName,
                                                 value,
                                                 reqNamespaceURI,
                                                 reqLocalName,
                                                 reqTraitNamespace,
                                                 reqTraitName);
        }
                      
    }

    /**
     * CompositeGraphicsNode handles the graphics node traits.
     * Other attributes are handled by the super class.
     *
     * Supported traits: stroke-width, stroke-miterlimit, stroke-dashoffset,
     * fill-rule, stroke-linejoin, stroke-linecap, display, visibility, 
     * color, fill, stroke, fill-opacity, stroke-opacity, stroke-dasharray,
     * opacity
     *
     * @param name the name of the trait to set.
     * @param value the value of the trait to set.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
     * trait's value cannot be specified as a String
     * @throws DOMException with error code INVALID_ACCESS_ERR if the input
     * value is an invalid value for the given trait or null.
     * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if
     * attempt is made to change readonly trait.
     */
    public void setTraitImpl(final String name, final String value)
        throws DOMException {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == name) {

            // ======================= stroke-width ===================== //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setFloatInherited(PROPERTY_STROKE_WIDTH, true);
            } else {
                setStrokeWidth(parsePositiveFloatTrait(name, value));
            }
        } else if (SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE
                   .equals(name)) {

            // ===================== stroke-miterlimit ================== //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setFloatInherited(PROPERTY_STROKE_MITER_LIMIT, true);
            } else {
                float miter = parseFloatTrait(name, value);
                if (miter < 1) {
                    throw illegalTraitValue(name, value);
                }
                setStrokeMiterLimit(miter);
            }

        } else if (SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE
                   .equals(name)) {

            // ===================== stroke-dashoffset ================== //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setFloatInherited(PROPERTY_STROKE_DASH_OFFSET, true);
            } else {
                setStrokeDashOffset(parseFloatTrait(name, value));
            }
        } else if (SVGConstants.SVG_FILL_RULE_ATTRIBUTE == name) { 

            // ========================= fill-rule ====================== //

            if (SVGConstants.CSS_NONZERO_VALUE.equals(value)) {
                setFillRule(GraphicsProperties.WIND_NON_ZERO);
            } else if (SVGConstants.CSS_EVENODD_VALUE.equals(value)) {
                setFillRule(GraphicsProperties.WIND_EVEN_ODD);
            } else if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setPackedInherited(PROPERTY_FILL_RULE, true);
            } else {
                throw illegalTraitValue(name, value);
            }
        } else if (SVGConstants.SVG_STROKE_LINEJOIN_ATTRIBUTE == name) {

            // ==================== stroke-linejoin ===================== //

            if (SVGConstants.CSS_MITER_VALUE.equals(value)) {
                setStrokeLineJoin(GraphicsProperties.JOIN_MITER);
            } else if (SVGConstants.CSS_ROUND_VALUE.equals(value)) {
                setStrokeLineJoin(GraphicsProperties.JOIN_ROUND);
            } else if (SVGConstants.CSS_BEVEL_VALUE.equals(value)) {
                setStrokeLineJoin(GraphicsProperties.JOIN_BEVEL);
            } else if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setPackedInherited(PROPERTY_STROKE_LINE_JOIN, true);
            } else {
                throw illegalTraitValue(name, value);
            }
        } else if (SVGConstants.SVG_STROKE_LINECAP_ATTRIBUTE == name) {

            // ====================== stroke-linecap ==================== //

            if (SVGConstants.CSS_BUTT_VALUE.equals(value)) {
                setStrokeLineCap(GraphicsProperties.CAP_BUTT);
            } else if (SVGConstants.CSS_ROUND_VALUE.equals(value)) {
                setStrokeLineCap(GraphicsProperties.CAP_ROUND);
            } else if (SVGConstants.CSS_SQUARE_VALUE.equals(value)) {
                setStrokeLineCap(GraphicsProperties.CAP_SQUARE);
            } else if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setPackedInherited(PROPERTY_STROKE_LINE_CAP, true);
            } else {
                throw illegalTraitValue(name, value);
            }
        } else if (SVGConstants.SVG_DISPLAY_ATTRIBUTE == name) {

            // ======================== display ========================= //

            if (SVGConstants.CSS_NONE_VALUE.equals(value)) {
                setDisplay(false);
            } else if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                if (!isInherited(PROPERTY_DISPLAY)) {
                    setPackedInherited(PROPERTY_DISPLAY, true);
                }
            } else if (SVGConstants.CSS_BLOCK_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_COMPACT_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_INLINE_TABLE_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_INLINE_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_LIST_ITEM_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_MARKER_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_RUN_IN_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_ROW_GROUP_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_HEADER_GROUP_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_FOOTER_GROUP_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_ROW_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_COLUMN_GROUP_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_COLUMN_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_CELL_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_TABLE_CAPTION_VALUE.equals(value)) {
                setDisplay(true);
            } else {
                throw illegalTraitValue(name, value);
            }
        } else if (SVGConstants.SVG_VISIBILITY_ATTRIBUTE == name) {

            // ======================= visibility ======================= //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setPackedInherited(PROPERTY_VISIBILITY, true);
            } else if (SVGConstants.CSS_HIDDEN_VALUE.equals(value)
                       ||
                       SVGConstants.CSS_COLLAPSE_VALUE.equals(value)) {
                setVisibility(false);
            } else if (SVGConstants.CSS_VISIBLE_VALUE.equals(value)) {
                setVisibility(true);
            } else {
                throw illegalTraitValue(name, value);
            }
        } else if (SVGConstants.SVG_COLOR_ATTRIBUTE == name) {

            // ========================= color ========================== //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setInherited(PROPERTY_COLOR, true);
            } else if (SVGConstants.CSS_NONE_VALUE.equals(value)) {
                // 'none' is not a legal value for 'color'
                throw illegalTraitValue(name, value);
            } else {
                RGB color = parseColorTrait
                    (SVGConstants.SVG_COLOR_ATTRIBUTE, value);
                setColor(color);
            }
        } else if (SVGConstants.SVG_FILL_ATTRIBUTE == name) {

            // ========================= fill =========================== //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setInherited(PROPERTY_FILL, true);
                setColorRelative(PROPERTY_FILL, false);
            } else if (SVGConstants.CSS_NONE_VALUE.equals(value)) {
                setFill(null);
            } else if (SVGConstants.CSS_CURRENTCOLOR_VALUE.equals(value)) {
                setColorRelative(PROPERTY_FILL, true);
                setInherited(PROPERTY_FILL, false);
            } else {
                PaintServer fill = parsePaintTrait
                    (SVGConstants.SVG_FILL_ATTRIBUTE, this, value);
                if (fill != null) {
                    setFill(fill);
                }
            }
        } else if (SVGConstants.SVG_STROKE_ATTRIBUTE == name) {

            // ========================= stroke ========================= //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setInherited(PROPERTY_STROKE, true);
                setColorRelative(PROPERTY_STROKE, false);
            } else if (SVGConstants.CSS_NONE_VALUE.equals(value)) {
                setStroke(null);
            } else if (SVGConstants.CSS_CURRENTCOLOR_VALUE.equals(value)) {
                setColorRelative(PROPERTY_STROKE, true);
                setInherited(PROPERTY_STROKE, false);
            } else {
                PaintServer stroke = parsePaintTrait
                    (SVGConstants.SVG_STROKE_ATTRIBUTE, this, value);
                if (stroke != null) {
                    setStroke(stroke);
                }
            }
        } else if (SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == name) {

            // ====================== fill-opacity ======================= //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setPackedInherited(PROPERTY_FILL_OPACITY, true);
            } else {
                setFillOpacity(parseFloatTrait(name, value));
            }
        } else if (SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE
                   .equals(name)) {

            // ================= stroke-opacity ========================= //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setPackedInherited(PROPERTY_STROKE_OPACITY, true);
            } else {
                setStrokeOpacity(parseFloatTrait(name, value));
            }
        } else if (SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE
                   .equals(name)) {

            // ==================== stroke-dasharray ==================== //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setInherited(PROPERTY_STROKE_DASH_ARRAY, true);
            } else if (SVGConstants.CSS_NONE_VALUE.equals(value)) {
                setStrokeDashArray(null);
            } else {
                setStrokeDashArray(parsePositiveFloatArrayTrait(name, value));
            }
        } else if (SVGConstants.SVG_OPACITY_ATTRIBUTE
                   .equals(name)) {

            // ================= opacity ========================= //

            if (SVGConstants.CSS_INHERIT_VALUE.equals(value)) {
                setPackedInherited(PROPERTY_OPACITY, true);
            } else {
                setOpacity(parseFloatTrait(name, value));
            }
        } else {
            super.setTraitImpl(name, value);
        }
    }

    /**
     * @param name the name of the trait to convert.
     * @param value the float trait value to convert.
     */
    String toStringTrait(final String name, final float[][] value) {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == name
            ||
            SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == name
            ||
            SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == name
            ||
            SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == name
            ||
            SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == name
            ||
            SVGConstants.SVG_OPACITY_ATTRIBUTE == name) {
            return Float.toString(value[0][0]);
        } else if (SVGConstants.SVG_COLOR_ATTRIBUTE == name
                   ||
                   SVGConstants.SVG_FILL_ATTRIBUTE == name
                   ||
                   SVGConstants.SVG_STROKE_ATTRIBUTE == name) {
            return toRGBString(name, value);
        } else if (SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE == name) {
            return toStringTrait(value[0]);
        } else {
            return super.toStringTrait(name, value);
        }
    }

    /**
     * Set the trait value as float.  
     *
     * Supported float traits: stroke-width, stroke-miterlimit,
     * stroke-dashoffset, fill-opacity, stroke-opacity, opacity.
     *
     * @param name the name of the trait to set.
     * @param value the value of the trait to set.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
     * trait's value cannot be specified as a float
     * @throws DOMException with error code INVALID_ACCESS_ERR if the input
     * value is an invalid value for the given trait.
     * @throws SecurityException if the application does not have the necessary
     * privilege rights to access this (SVG) content.
     */
    public void setFloatTraitImpl(final String name, final float value)
        throws DOMException {
        if (SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE == name) {
            checkPositive(name, value);
            setStrokeWidth(value);
        } else if (SVGConstants.SVG_STROKE_MITERLIMIT_ATTRIBUTE == name) {
            if (value < 1) {
                throw illegalTraitValue(name, Float.toString(value));
            }
            setStrokeMiterLimit(value);
        } else if (SVGConstants.SVG_STROKE_DASHOFFSET_ATTRIBUTE == name) {
            setStrokeDashOffset(value);
        } else if (SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE == name) {
            setFillOpacity(value);
        } else if (SVGConstants.SVG_STROKE_OPACITY_ATTRIBUTE == name) {
            setStrokeOpacity(value);
        } else if (SVGConstants.SVG_OPACITY_ATTRIBUTE == name) {
            setOpacity(value);
        } else {
            super.setFloatTraitImpl(name, value);
        }
    }

    /**
     * Set the trait value as {@link org.w3c.dom.svg.SVGRGBColor SVGRGBColor}.
     *
     * Supported color traits: color, fill, stroke
     *
     * @param name the name of the trait to set.
     * @param value the value of the trait to set.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
     * trait's value cannot be specified as an {@link
     * org.w3c.dom.svg.SVGRGBColor SVGRGBColor}
     * @throws DOMException with error code INVALID_ACCESS_ERR if the input
     * value is null.
     * @throws SecurityException if the application does not have the necessary
     * privilege rights to access this (SVG) content.
     */
    void setRGBColorTraitImpl(final String name, final SVGRGBColor color)
        throws DOMException {
        try {
            // We use .equals here because the name string may not have been
            // interned.
            if (SVGConstants.SVG_FILL_ATTRIBUTE.equals(name)) {
                setFill((RGB) color);
            } else if (SVGConstants.SVG_STROKE_ATTRIBUTE .equals(name)) {
                setStroke((RGB) color);
            } else if (SVGConstants.SVG_COLOR_ATTRIBUTE .equals(name)) {
                setColor((RGB) color);
            } else {
                super.setRGBColorTraitImpl(name, color);
            } 
        } catch (IllegalArgumentException iae) {
            throw new DOMException(DOMException.INVALID_ACCESS_ERR, 
                                   iae.getMessage());
        }
    }

    /**
     * @param paintType the key provided by the PaintTarget when it subscribed 
     *        to associated PaintServer.
     * @param paintServer the PaintServer generating the update.
     */
    public void onPaintServerUpdate(final String paintType,
                                    final PaintServer paintServer) {
        if (paintType == SVGConstants.SVG_FILL_ATTRIBUTE) {
            setFill(paintServer);
        } else if (paintType == SVGConstants.SVG_STROKE_ATTRIBUTE) {
            setStroke(paintServer);
        } else {
            throw new Error();
        }
    }
}