/**
   * Builds using the specified BridgeContext and element, the specified graphics node.
   *
   * @param ctx the bridge context to use
   * @param e the element that describes the graphics node to build
   * @param node the graphics node to build
   */
  public void buildGraphicsNode(BridgeContext ctx, Element e, GraphicsNode node) {
    CompositeGraphicsNode cgn = (CompositeGraphicsNode) node;

    // build flowRegion shapes
    boolean isStatic = !ctx.isDynamic();
    if (isStatic) {
      flowRegionNodes = new HashMap();
    } else {
      regionChangeListener = new RegionChangeListener();
    }
    CompositeGraphicsNode cgn2 = (CompositeGraphicsNode) cgn.get(0);
    GVTBuilder builder = ctx.getGVTBuilder();
    for (Node n = getFirstChild(e); n != null; n = getNextSibling(n)) {
      if (n instanceof SVGOMFlowRegionElement) {
        for (Node m = getFirstChild(n); m != null; m = getNextSibling(m)) {
          if (m.getNodeType() != Node.ELEMENT_NODE) {
            continue;
          }
          GraphicsNode gn = builder.build(ctx, (Element) m);
          if (gn != null) {
            cgn2.add(gn);
            if (isStatic) {
              flowRegionNodes.put(m, gn);
            }
          }
        }

        if (!isStatic) {
          AbstractNode an = (AbstractNode) n;
          XBLEventSupport es = (XBLEventSupport) an.initializeEventSupport();
          es.addImplementationEventListenerNS(
              SVG_NAMESPACE_URI, "shapechange", regionChangeListener, false);
        }
      }
    }

    // build text node
    GraphicsNode tn = (GraphicsNode) cgn.get(1);
    super.buildGraphicsNode(ctx, e, tn);

    // Drop references once static build is completed.
    flowRegionNodes = null;
  }
  /**
   * Creates a <tt>GraphicsNode</tt> according to the specified parameters.
   *
   * @param ctx the bridge context to use
   * @param e the element that describes the graphics node to build
   * @return a graphics node that represents the specified element
   */
  public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
    // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
    if (!SVGUtilities.matchUserAgent(e, ctx.getUserAgent())) {
      return null;
    }

    CompositeGraphicsNode cgn = new CompositeGraphicsNode();

    // 'transform'
    String s = e.getAttributeNS(null, SVG_TRANSFORM_ATTRIBUTE);
    if (s.length() != 0) {
      cgn.setTransform(SVGUtilities.convertTransform(e, SVG_TRANSFORM_ATTRIBUTE, s, ctx));
    }
    // 'visibility'
    cgn.setVisible(CSSUtilities.convertVisibility(e));

    // 'text-rendering' and 'color-rendering'
    RenderingHints hints = null;
    hints = CSSUtilities.convertColorRendering(e, hints);
    hints = CSSUtilities.convertTextRendering(e, hints);
    if (hints != null) {
      cgn.setRenderingHints(hints);
    }

    // first child holds the flow region nodes
    CompositeGraphicsNode cgn2 = new CompositeGraphicsNode();
    cgn.add(cgn2);

    // second child is the text node
    FlowTextNode tn = (FlowTextNode) instantiateGraphicsNode();
    tn.setLocation(getLocation(ctx, e));

    // specify the text painter to use
    if (ctx.getTextPainter() != null) {
      tn.setTextPainter(ctx.getTextPainter());
    }
    textNode = tn;
    cgn.add(tn);

    associateSVGContext(ctx, e, cgn);

    // traverse the children to add SVGContext
    Node child = getFirstChild(e);
    while (child != null) {
      if (child.getNodeType() == Node.ELEMENT_NODE) {
        addContextToChild(ctx, (Element) child);
      }
      child = getNextSibling(child);
    }

    return cgn;
  }