/**
   * Constructor for top-level document.
   *
   * @param externalContext external context to obtain request path, properties, etc.
   * @param contentHandler resulting static state document
   */
  public XFormsExtractorContentHandler(
      ExternalContext externalContext, ContentHandler contentHandler) {
    super(contentHandler);
    this.externalContext = externalContext;
    this.ignoreRootElement = false;

    final ExternalContext.Request request = externalContext.getRequest();

    // Remember if filter provided separate deployment information
    isSeparateDeployment =
        "separate"
            .equals(
                request.getAttributesMap().get(OrbeonXFormsFilter.OPS_XFORMS_RENDERER_DEPLOYMENT));

    // Try to get request context path
    {
      // First try context path passed by the filter
      requestContextPath =
          (String)
              request
                  .getAttributesMap()
                  .get(OrbeonXFormsFilter.OPS_XFORMS_RENDERER_REQUEST_CONTEXT_PATH);

      // Otherwise just use the request's context path
      if (requestContextPath == null) requestContextPath = request.getContextPath();
    }

    // Create xml:base stack
    try {
      final String rootXMLBase;
      {
        // It is possible to override the base URI by setting a request attribute. This is used by
        // OPSXFormsFilter.
        final String rendererBaseURI =
            (String)
                request
                    .getAttributesMap()
                    .get(OrbeonXFormsFilter.OPS_XFORMS_RENDERER_BASE_URI_ATTRIBUTE_NAME);
        if (rendererBaseURI != null) rootXMLBase = rendererBaseURI;
        else rootXMLBase = request.getRequestPath();
      }
      xmlBaseStack.push(new URI(null, null, rootXMLBase, null));
    } catch (URISyntaxException e) {
      throw new ValidationException(e, new LocationData(locator));
    }
  }