private XFormsInstance checkInstanceToUpdate(
      PropertyContext propertyContext, XFormsModelSubmission.SubmissionParameters p) {
    XFormsInstance updatedInstance;
    final NodeInfo destinationNodeInfo =
        submission.evaluateTargetRef(
            propertyContext,
            p.xpathContext,
            submission.findReplaceInstanceNoTargetref(p.refInstance),
            p.submissionElementContextItem);

    if (destinationNodeInfo == null) {
      // Throw target-error

      // XForms 1.1: "If the processing of the targetref attribute fails,
      // then submission processing ends after dispatching the event
      // xforms-submit-error with an error-type of target-error."

      throw new XFormsSubmissionException(
          submission,
          "targetref attribute doesn't point to an element for replace=\"instance\".",
          "processing targetref attribute",
          new XFormsSubmitErrorEvent(
              propertyContext, submission, XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR, null));
    }

    updatedInstance = submission.getContainingDocument().getInstanceForNode(destinationNodeInfo);
    if (updatedInstance == null
        || !updatedInstance.getInstanceRootElementInfo().isSameNodeInfo(destinationNodeInfo)) {
      // Only support replacing the root element of an instance
      // TODO: in the future, check on resolvedXXFormsReadonly to implement this restriction only
      // when using a readonly instance
      throw new XFormsSubmissionException(
          submission,
          "targetref attribute must point to an instance root element when using cached/shared instance replacement.",
          "processing targetref attribute",
          new XFormsSubmitErrorEvent(
              propertyContext, submission, XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR, null));
    }

    if (XFormsServer.logger.isDebugEnabled())
      submission
          .getContainingDocument()
          .logDebug(
              "submission",
              "using instance from application shared instance cache",
              "instance",
              updatedInstance.getEffectiveId());
    return updatedInstance;
  }
  public void replace(
      PropertyContext propertyContext,
      ConnectionResult connectionResult,
      XFormsModelSubmission.SubmissionParameters p,
      XFormsModelSubmission.SecondPassParameters p2) {

    // Set new instance document to replace the one submitted

    final XFormsInstance replaceInstanceNoTargetref =
        submission.findReplaceInstanceNoTargetref(p.refInstance);
    if (replaceInstanceNoTargetref == null) {

      // Replacement instance or node was specified but not found
      //
      // Not sure what's the right thing to do with 1.1, but this could be done
      // as part of the model's static analysis if the instance value is not
      // obtained through AVT, and dynamically otherwise. However, in the dynamic
      // case, I think that this should be a (currently non-specified by XForms)
      // xforms-binding-error.

      submission
          .getXBLContainer(containingDocument)
          .dispatchEvent(
              propertyContext, new XFormsBindingExceptionEvent(containingDocument, submission));
    } else {

      final NodeInfo destinationNodeInfo =
          submission.evaluateTargetRef(
              propertyContext,
              p.xpathContext,
              replaceInstanceNoTargetref,
              p.submissionElementContextItem);

      if (destinationNodeInfo == null) {
        // Throw target-error

        // XForms 1.1: "If the processing of the targetref attribute fails,
        // then submission processing ends after dispatching the event
        // xforms-submit-error with an error-type of target-error."

        throw new XFormsSubmissionException(
            submission,
            "targetref attribute doesn't point to an element for replace=\"instance\".",
            "processing targetref attribute",
            new XFormsSubmitErrorEvent(
                containingDocument,
                propertyContext,
                submission,
                XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR,
                connectionResult));
      }

      // This is the instance which is effectively going to be updated
      final XFormsInstance updatedInstance =
          containingDocument.getInstanceForNode(destinationNodeInfo);
      if (updatedInstance == null) {
        throw new XFormsSubmissionException(
            submission,
            "targetref attribute doesn't point to an element in an existing instance for replace=\"instance\".",
            "processing targetref attribute",
            new XFormsSubmitErrorEvent(
                containingDocument,
                propertyContext,
                submission,
                XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR,
                connectionResult));
      }

      // Whether the destination node is the root element of an instance
      final boolean isDestinationRootElement =
          updatedInstance.getInstanceRootElementInfo().isSameNodeInfo(destinationNodeInfo);
      if (p2.isReadonly && !isDestinationRootElement) {
        // Only support replacing the root element of an instance when using a shared instance
        throw new XFormsSubmissionException(
            submission,
            "targetref attribute must point to instance root element when using read-only instance replacement.",
            "processing targetref attribute",
            new XFormsSubmitErrorEvent(
                containingDocument,
                propertyContext,
                submission,
                XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR,
                connectionResult));
      }

      final IndentedLogger detailsLogger = getDetailsLogger(p, p2);

      // Obtain root element to insert
      final NodeInfo newDocumentRootElement;
      final XFormsInstance newInstance;
      {
        // Create resulting instance whether entire instance is replaced or not, because this:
        // 1. Wraps a Document within a DocumentInfo if needed
        // 2. Performs text nodes adjustments if needed
        if (!p2.isReadonly) {
          // Resulting instance must not be read-only

          if (detailsLogger.isDebugEnabled())
            detailsLogger.logDebug(
                "",
                "replacing instance with mutable instance",
                "instance",
                updatedInstance.getEffectiveId());

          newInstance =
              new XFormsInstance(
                  updatedInstance.getEffectiveModelId(),
                  updatedInstance.getId(),
                  (Document) resultingDocument,
                  connectionResult.resourceURI,
                  resultingRequestBodyHash,
                  p2.username,
                  p2.password,
                  p2.isCache,
                  p2.timeToLive,
                  updatedInstance.getValidation(),
                  p2.isHandleXInclude,
                  XFormsProperties.isExposeXPathTypes(containingDocument));
        } else {
          // Resulting instance must be read-only

          if (detailsLogger.isDebugEnabled())
            detailsLogger.logDebug(
                "",
                "replacing instance with read-only instance",
                "instance",
                updatedInstance.getEffectiveId());

          newInstance =
              new ReadonlyXFormsInstance(
                  updatedInstance.getEffectiveModelId(),
                  updatedInstance.getId(),
                  (DocumentInfo) resultingDocument,
                  connectionResult.resourceURI,
                  resultingRequestBodyHash,
                  p2.username,
                  p2.password,
                  p2.isCache,
                  p2.timeToLive,
                  updatedInstance.getValidation(),
                  p2.isHandleXInclude,
                  XFormsProperties.isExposeXPathTypes(containingDocument));
        }
        newDocumentRootElement = newInstance.getInstanceRootElementInfo();
      }

      // Perform insert/delete. This will dispatch xforms-insert/xforms-delete events.
      // "the replacement is performed by an XForms action that performs some
      // combination of node insertion and deletion operations that are
      // performed by the insert action (10.3 The insert Element) and the
      // delete action"

      if (isDestinationRootElement) {
        // Optimized insertion for instance root element replacement

        // Handle new instance and associated event markings
        final XFormsModel replaceModel = newInstance.getModel(containingDocument);
        replaceModel.handleUpdatedInstance(propertyContext, newInstance, newDocumentRootElement);

        // Dispatch xforms-delete event
        // NOTE: Do NOT dispatch so we are compatible with the regular root element replacement
        // (see below). In the future, we might want to dispatch this, especially if
        // XFormsInsertAction dispatches xforms-delete when removing the root element
        // updatedInstance.getXBLContainer(containingDocument).dispatchEvent(pipelineContext, new
        // XFormsDeleteEvent(updatedInstance, Collections.singletonList(destinationNodeInfo), 1));

        // Dispatch xforms-insert event
        // NOTE: use the root node as insert location as it seems to make more sense than pointing
        // to the earlier root element
        newInstance
            .getXBLContainer(containingDocument)
            .dispatchEvent(
                propertyContext,
                new XFormsInsertEvent(
                    containingDocument,
                    newInstance,
                    Collections.singletonList((Item) newDocumentRootElement),
                    null,
                    newDocumentRootElement.getDocumentRoot(),
                    "after",
                    null,
                    null,
                    true));

      } else {
        // Generic insertion

        final List<NodeInfo> destinationCollection = Collections.singletonList(destinationNodeInfo);

        // Perform the insertion

        // Insert before the target node, so that the position of the inserted node
        // wrt its parent does not change after the target node is removed
        final List insertedNode =
            XFormsInsertAction.doInsert(
                propertyContext,
                containingDocument,
                detailsLogger,
                "before",
                destinationCollection,
                destinationNodeInfo.getParent(),
                Collections.singletonList(newDocumentRootElement),
                1,
                false,
                true);

        if (!destinationNodeInfo
            .getParent()
            .isSameNodeInfo(destinationNodeInfo.getDocumentRoot())) {
          // The node to replace is NOT a root element

          // Perform the deletion of the selected node
          XFormsDeleteAction.doDelete(
              propertyContext, containingDocument, detailsLogger, destinationCollection, 1, true);
        }

        // Perform model instance update
        // Handle new instance and associated event markings
        // NOTE: The inserted node NodeWrapper.index might be out of date at this point because:
        // * doInsert() dispatches an event which might itself change the instance
        // * doDelete() does as well
        // Does this mean that we should check that the node is still where it should be?
        final XFormsModel updatedModel = updatedInstance.getModel(containingDocument);
        updatedModel.handleUpdatedInstance(
            propertyContext, updatedInstance, (NodeInfo) insertedNode.get(0));
      }

      // Dispatch xforms-submit-done
      dispatchSubmitDone(propertyContext, connectionResult);
    }
  }