private Stage2CacheableState readStaticState(
      PipelineContext pipelineContext,
      ExternalContext externalContext,
      IndentedLogger indentedLogger,
      XFormsStaticState[] staticState) {

    final StaticStateBits staticStateBits =
        new StaticStateBits(pipelineContext, externalContext, indentedLogger, null);

    {
      final XFormsStaticState cachedState =
          XFormsStaticStateCache.instance()
              .getDocument(pipelineContext, staticStateBits.staticStateDigest);
      if (cachedState != null && cachedState.getMetadata().checkBindingsIncludes()) {
        // Found static state in cache
        indentedLogger.logDebug("", "found up-to-date static state by digest in cache");

        staticState[0] = cachedState;
      } else {
        // Not found static state in cache OR it is out of date, create and initialize static state
        // object
        // NOTE: In out of date case, could clone static state and reprocess instead?
        if (cachedState != null)
          indentedLogger.logDebug("", "found out-of-date static state by digest in cache");
        else indentedLogger.logDebug("", "did not find static state by digest in cache");

        staticState[0] =
            new XFormsStaticState(
                pipelineContext,
                staticStateBits.staticStateDocument,
                staticStateBits.staticStateDigest,
                staticStateBits.metadata);

        // Store in cache
        XFormsStaticStateCache.instance().storeDocument(pipelineContext, staticState[0]);
      }
    }

    // Update input dependencies object
    return new Stage2CacheableState(
        staticStateBits.annotatedTemplate, staticStateBits.staticStateDigest);
  }
  /**
   * Restore an XFormsContainingDocument from XFormsState only.
   *
   * <p>Used by XFormsStateManager.
   *
   * @param xformsState XFormsState containing static and dynamic state
   * @param disableUpdates whether to disable updates (for recreating initial document upon browser
   *     back)
   */
  public XFormsContainingDocument(XFormsState xformsState, boolean disableUpdates) {
    super();

    // 1. Restore the static state
    {
      final scala.Option<String> staticStateDigest = xformsState.staticStateDigest();

      if (staticStateDigest.isDefined()) {
        final XFormsStaticState cachedState =
            XFormsStaticStateCache.instance().getDocument(staticStateDigest.get());
        if (cachedState != null) {
          // Found static state in cache
          indentedLogger().logDebug("", "found static state by digest in cache");
          this.staticState = cachedState;
        } else {
          // Not found static state in cache, create static state from input
          indentedLogger().logDebug("", "did not find static state by digest in cache");
          indentedLogger().startHandleOperation("initialization", "restoring static state");
          this.staticState =
              XFormsStaticStateImpl.restore(staticStateDigest, xformsState.staticState());
          indentedLogger().endHandleOperation();

          // Store in cache
          XFormsStaticStateCache.instance().storeDocument(this.staticState);
        }

        assert this.staticState.isServerStateHandling();
      } else {
        // Not digest provided, create static state from input
        indentedLogger().logDebug("", "did not find static state by digest in cache");
        this.staticState =
            XFormsStaticStateImpl.restore(staticStateDigest, xformsState.staticState());

        assert this.staticState.isClientStateHandling();
      }

      this.staticOps = new StaticStateGlobalOps(staticState.topLevelPart());
      this.xpathDependencies = Version.instance().createUIDependencies(this);

      this.supportUpdates = !disableUpdates && !isNoUpdates();
    }

    // 2. Restore the dynamic state
    indentedLogger().startHandleOperation("initialization", "restoring containing document");
    try {
      restoreDynamicState(xformsState.dynamicState());
    } catch (Exception e) {
      throw OrbeonLocationException.wrapException(
          e, new ExtendedLocationData(null, "re-initializing XForms containing document"));
    }
    indentedLogger().endHandleOperation();
  }
  /**
   * Create an XFormsContainingDocument from an XFormsStaticState object.
   *
   * <p>Used by XFormsToXHTML.
   *
   * @param staticState static state object
   * @param uriResolver URIResolver for loading instances during initialization (and possibly more,
   *     such as schemas and "GET" submissions upon initialization)
   * @param response optional response for handling replace="all" during initialization
   */
  public XFormsContainingDocument(
      XFormsStaticState staticState,
      XFormsURIResolver uriResolver,
      ExternalContext.Response response) {
    super();

    // Create UUID for this document instance
    this.uuid = SecureUtils.randomHexId();

    // Initialize request information
    {
      initializeRequestInformation();
      initializePathMatchers();
    }

    indentedLogger()
        .startHandleOperation(
            "initialization",
            "creating new ContainingDocument (static state object provided).",
            "uuid",
            this.uuid);
    {
      // Remember static state
      this.staticState = staticState;
      this.staticOps = new StaticStateGlobalOps(staticState.topLevelPart());

      // NOTE: template is not stored right away, as it depends on the evaluation of the noscript
      // property.

      this.xpathDependencies = Version.instance().createUIDependencies(this);

      // Whether we support updates
      // NOTE: Reading the property requires the static state set above
      this.supportUpdates = !isNoUpdates();

      // Remember parameters used during initialization
      this.uriResolver = uriResolver;
      this.response = response;
      this.initializing = true;

      // Initialize the containing document
      try {
        initialize();
      } catch (Exception e) {
        throw OrbeonLocationException.wrapException(
            e, new ExtendedLocationData(null, "initializing XForms containing document"));
      }
    }
    indentedLogger().endHandleOperation();
  }
 @Override
 public Scope innerScope() {
   // Do it here because at construction time, we don't yet have access to the static state!
   return staticState.topLevelPart().startScope();
 }
 @Override
 public PartAnalysis partAnalysis() {
   return staticState.topLevelPart();
 }
  private void gatherInputDependencies(
      PipelineContext pipelineContext,
      XFormsContainingDocument containingDocument,
      IndentedLogger indentedLogger,
      Stage1CacheableState stage1CacheableState) {

    final String forwardSubmissionHeaders =
        XFormsProperties.getForwardSubmissionHeaders(containingDocument);

    // Add static instance source dependencies for top-level models
    // TODO: check all models/instances
    final XFormsStaticState staticState = containingDocument.getStaticState();
    for (final Model model :
        staticState.getModelsForScope(staticState.getXBLBindings().getTopLevelScope())) {
      for (final Instance instance : model.instancesMap().values()) {
        if (instance.dependencyURL() != null) {

          final String resolvedDependencyURL =
              XFormsUtils.resolveServiceURL(
                  pipelineContext,
                  containingDocument,
                  instance.element(),
                  instance.dependencyURL(),
                  ExternalContext.Response.REWRITE_MODE_ABSOLUTE);

          if (!instance.isCacheHint()) {
            stage1CacheableState.addReference(
                null,
                resolvedDependencyURL,
                instance.xxformsUsername(),
                instance.xxformsPassword(),
                instance.xxformsPassword(),
                forwardSubmissionHeaders);

            if (indentedLogger.isDebugEnabled())
              indentedLogger.logDebug(
                  "",
                  "adding document cache dependency for non-cacheable instance",
                  "instance URI",
                  resolvedDependencyURL);

          } else {
            // Don't add the dependency as we don't want the instance URI to be hit
            // For all practical purposes, globally shared instances must remain constant!
            if (indentedLogger.isDebugEnabled())
              indentedLogger.logDebug(
                  "",
                  "not adding document cache dependency for cacheable instance",
                  "instance URI",
                  resolvedDependencyURL);
          }
        }
      }
    }

    // Set caching dependencies if the input was actually read
    // TODO: check all models/instances
    // Q: should use static dependency information instead? what about schema imports and instance
    // replacements?
    for (final XFormsModel currentModel : containingDocument.getModels()) {
      // Add schema dependencies
      final String[] schemaURIs = currentModel.getSchemaURIs();
      // TODO: We should also use dependencies computed in XFormsModelSchemaValidator.SchemaInfo
      if (schemaURIs != null) {
        for (final String currentSchemaURI : schemaURIs) {
          if (indentedLogger.isDebugEnabled())
            indentedLogger.logDebug(
                "", "adding document cache dependency for schema", "schema URI", currentSchemaURI);

          stage1CacheableState.addReference(
              null,
              currentSchemaURI,
              null,
              null,
              null,
              forwardSubmissionHeaders); // TODO: support username / password on schema refs
        }
      }
    }
    // TODO: Add @src attributes from controls? Not used often.

    // Set caching dependencies for XBL inclusions
    {
      final XFormsStaticState.Metadata metadata = containingDocument.getStaticState().getMetadata();
      final Set<String> includes = metadata.getBindingsIncludes();
      if (includes != null) {
        for (final String include : includes) {
          stage1CacheableState.addReference(null, "oxf:" + include, null, null, null, null);
        }
      }
    }
  }
  private void doIt(
      final PipelineContext pipelineContext,
      XMLReceiver xmlReceiver,
      final URIProcessorOutputImpl processorOutput,
      String outputName) {

    final ExternalContext externalContext = XFormsUtils.getExternalContext(pipelineContext);
    final IndentedLogger indentedLogger =
        XFormsContainingDocument.getIndentedLogger(
            XFormsToXHTML.logger, XFormsServer.getLogger(), LOGGING_CATEGORY);

    // ContainingDocument and XFormsState created below
    final XFormsContainingDocument[] containingDocument = new XFormsContainingDocument[1];
    final boolean[] cachedStatus = new boolean[] {false};

    final Stage2CacheableState stage2CacheableState;
    if (TEST_STATE == null) {

      // Read and try to cache the complete XForms+XHTML document with annotations
      stage2CacheableState =
          (Stage2CacheableState)
              readCacheInputAsObject(
                  pipelineContext,
                  getInputByName(INPUT_ANNOTATED_DOCUMENT),
                  new CacheableInputReader() {
                    public Object read(
                        PipelineContext pipelineContext, ProcessorInput processorInput) {

                      // Compute annotated XForms document + static state document
                      final Stage1CacheableState stage1CacheableState = new Stage1CacheableState();
                      final Stage2CacheableState stage2CacheableState;
                      final XFormsStaticState[] staticState = new XFormsStaticState[1];
                      {
                        // Store dependencies container in state before reading
                        ((Stage2TransientState) XFormsToXHTML.this.getState(pipelineContext))
                                .stage1CacheableState =
                            stage1CacheableState;

                        // Read static state from input
                        stage2CacheableState =
                            readStaticState(
                                pipelineContext, externalContext, indentedLogger, staticState);
                      }

                      // Create containing document and initialize XForms engine
                      // NOTE: Create document here so we can do appropriate analysis of caching
                      // dependencies
                      final XFormsURIResolver uriResolver =
                          new XFormsURIResolver(
                              XFormsToXHTML.this,
                              processorOutput,
                              pipelineContext,
                              INPUT_ANNOTATED_DOCUMENT,
                              URLGenerator.DEFAULT_HANDLE_XINCLUDE);
                      containingDocument[0] =
                          new XFormsContainingDocument(
                              pipelineContext,
                              staticState[0],
                              stage2CacheableState.getAnnotatedTemplate(),
                              uriResolver);

                      // Gather set caching dependencies
                      gatherInputDependencies(
                          pipelineContext,
                          containingDocument[0],
                          indentedLogger,
                          stage1CacheableState);

                      return stage2CacheableState;
                    }

                    @Override
                    public void foundInCache() {
                      cachedStatus[0] = true;
                    }
                  },
                  false);

      TEST_STATE = DO_TEST_STATE ? stage2CacheableState : null;

    } else {
      stage2CacheableState = TEST_STATE;
    }

    try {
      // Create containing document if not done yet
      if (containingDocument[0] == null) {
        assert cachedStatus[0];
        // In this case, we found the static state digest and more in the cache, but we must now
        // create a new XFormsContainingDocument from this information
        indentedLogger.logDebug(
            "",
            "annotated document and static state digest obtained from cache",
            "digest",
            stage2CacheableState.getStaticStateDigest());

        final XFormsStaticState staticState;
        {
          final XFormsStaticState cachedState =
              XFormsStaticStateCache.instance()
                  .getDocument(pipelineContext, stage2CacheableState.getStaticStateDigest());
          if (cachedState != null && cachedState.getMetadata().checkBindingsIncludes()) {
            // Found static state in cache
            indentedLogger.logDebug("", "found up-to-date static state by digest in cache");

            staticState = cachedState;
          } else {
            // Not found static state in cache OR it is out of date, create static state from input
            // NOTE: In out of date case, could clone static state and reprocess instead?
            if (cachedState != null)
              indentedLogger.logDebug("", "found out-of-date static state by digest in cache");
            else indentedLogger.logDebug("", "did not find static state by digest in cache");

            final StaticStateBits staticStateBits =
                new StaticStateBits(
                    pipelineContext,
                    externalContext,
                    indentedLogger,
                    stage2CacheableState.getStaticStateDigest());
            staticState =
                new XFormsStaticState(
                    pipelineContext,
                    staticStateBits.staticStateDocument,
                    stage2CacheableState.getStaticStateDigest(),
                    staticStateBits.metadata);

            // Store in cache
            XFormsStaticStateCache.instance().storeDocument(pipelineContext, staticState);
          }
        }

        final XFormsURIResolver uriResolver =
            new XFormsURIResolver(
                XFormsToXHTML.this,
                processorOutput,
                pipelineContext,
                INPUT_ANNOTATED_DOCUMENT,
                URLGenerator.DEFAULT_HANDLE_XINCLUDE);
        containingDocument[0] =
            new XFormsContainingDocument(
                pipelineContext,
                staticState,
                stage2CacheableState.getAnnotatedTemplate(),
                uriResolver);
      } else {
        assert !cachedStatus[0];
        indentedLogger.logDebug(
            "", "annotated document and static state digest not obtained from cache.");
      }

      // Output resulting document
      if (outputName.equals("document")) {
        // Normal case where we output XHTML
        outputResponseDocument(
            pipelineContext,
            externalContext,
            indentedLogger,
            stage2CacheableState.getAnnotatedTemplate(),
            containingDocument[0],
            xmlReceiver);
      } else {
        // Output in test mode
        testOutputResponseState(
            pipelineContext, containingDocument[0], indentedLogger, xmlReceiver);
      }

      // Notify state manager
      XFormsStateManager.instance().afterInitialResponse(pipelineContext, containingDocument[0]);

    } catch (Throwable e) {
      indentedLogger.logDebug("", "throwable caught during initialization.");
      throw new OXFException(e);
    }
  }