private Object deserializeInstance( IndentedLogger indentedLogger, boolean isReadonly, boolean isHandleXInclude, ConnectionResult connectionResult) throws Exception { final Object resultingDocument; // 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 try { if (!isReadonly) { // Resulting instance must not be read-only // TODO: What about configuring validation? And what default to choose? resultingDocument = TransformerUtils.readDom4j( connectionResult.getResponseInputStream(), connectionResult.resourceURI, isHandleXInclude, true); if (indentedLogger.isDebugEnabled()) indentedLogger.logDebug("", "deserializing to mutable instance"); } else { // Resulting instance must be read-only // TODO: What about configuring validation? And what default to choose? // NOTE: isApplicationSharedHint is always false when get get here. // isApplicationSharedHint="true" is handled above. resultingDocument = TransformerUtils.readTinyTree( XPathCache.getGlobalConfiguration(), connectionResult.getResponseInputStream(), connectionResult.resourceURI, isHandleXInclude, true); if (indentedLogger.isDebugEnabled()) indentedLogger.logDebug("", "deserializing to read-only instance"); } } catch (Exception e) { throw new XFormsSubmissionException( submission, e, "xf:submission: exception while reading XML response.", "processing instance replacement", new XFormsSubmitErrorEvent( submission, XFormsSubmitErrorEvent.PARSE_ERROR(), connectionResult)); } return resultingDocument; }
public void deserialize( ConnectionResult connectionResult, XFormsModelSubmission.SubmissionParameters p, XFormsModelSubmission.SecondPassParameters p2) throws Exception { // Deserialize here so it can run in parallel if (XMLUtils.isXMLMediatype(connectionResult.getResponseMediaType())) { // XML media type final IndentedLogger detailsLogger = getDetailsLogger(p, p2); resultingDocumentOrDocumentInfo = deserializeInstance(detailsLogger, p2.isReadonly, p2.isHandleXInclude, connectionResult); } else { // Other media type is not allowed throw new XFormsSubmissionException( submission, "Body received with non-XML media type for replace=\"instance\": " + connectionResult.getResponseMediaType(), "processing instance replacement", new XFormsSubmitErrorEvent( submission, XFormsSubmitErrorEvent.RESOURCE_ERROR(), connectionResult)); } }
public Runnable replace( final 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. // // Another option would be to dispatch, at runtime, an xxforms-binding-error event. // xforms-submit-error is // consistent with targetref, so might be better. throw new XFormsSubmissionException( submission, "instance attribute doesn't point to an existing instance for replace=\"instance\".", "processing instance attribute", new XFormsSubmitErrorEvent( submission, XFormsSubmitErrorEvent.TARGET_ERROR(), connectionResult)); } else { final NodeInfo destinationNodeInfo = submission.evaluateTargetRef( 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( submission, XFormsSubmitErrorEvent.TARGET_ERROR(), connectionResult)); } // This is the instance which is effectively going to be updated final XFormsInstance instanceToUpdate = containingDocument.getInstanceForNode(destinationNodeInfo); if (instanceToUpdate == 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( submission, XFormsSubmitErrorEvent.TARGET_ERROR(), connectionResult)); } // Whether the destination node is the root element of an instance final boolean isDestinationRootElement = instanceToUpdate.instanceRoot().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( submission, XFormsSubmitErrorEvent.TARGET_ERROR(), connectionResult)); } final IndentedLogger detailsLogger = getDetailsLogger(p, p2); // Obtain root element to insert if (detailsLogger.isDebugEnabled()) detailsLogger.logDebug( "", p2.isReadonly ? "replacing instance with read-only instance" : "replacing instance with mutable instance", "instance", instanceToUpdate.getEffectiveId()); // 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" // NOTE: As of 2009-03-18 decision, XForms 1.1 specifies that deferred event handling flags // are set instead of // performing RRRR directly. final DocumentInfo newDocumentInfo = wrappedDocumentInfo != null ? wrappedDocumentInfo : XFormsInstance.createDocumentInfo( resultingDocumentOrDocumentInfo, instanceToUpdate.instance().exposeXPathTypes()); if (isDestinationRootElement) { // Optimized insertion for instance root element replacement instanceToUpdate.replace( newDocumentInfo, true, Option.<InstanceCaching>apply(instanceCaching), p2.isReadonly); } else { // Generic insertion instanceToUpdate.markModified(); final Item newDocumentRootElement = DataModel.firstChildElement(newDocumentInfo); 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 // This will also mark a structural change // FIXME: Replace logic should use doReplace and xxforms-replace event XFormsInsertAction.doInsert( containingDocument, detailsLogger, "before", destinationCollection, destinationNodeInfo.getParent(), Collections.singletonList(newDocumentRootElement), 1, false, true); // Perform the deletion of the selected node XFormsDeleteAction.doDelete( containingDocument, detailsLogger, destinationCollection, 1, true); // Update model instance // 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? } // Dispatch xforms-submit-done return submission.sendSubmitDone(connectionResult); } }