//  [java]
    public boolean save(XWikiContext context) throws XWikiException
    {
        XWiki xwiki = context.getWiki();
        XWikiRequest request = context.getRequest();
        XWikiDocument doc = context.getDoc();
        EditForm form = (EditForm) context.getForm();

        // Check save session
        int sectionNumber = 0;
        if (request.getParameter("section") != null && xwiki.hasSectionEdit(context)) {
            sectionNumber = Integer.parseInt(request.getParameter("section"));
        }

        if (doc.isNew() && !this.isEntityReferenceNameValid(doc.getDocumentReference())) {
            context.put("message", "entitynamevalidation.create.invalidname");
            context.put("messageParameters",
                new Object[] { getLocalSerializer().serialize(doc.getDocumentReference())});
            return true;
        }

        XWikiDocument originalDoc = doc;

        // We need to clone this document first, since a cached storage would return the same object for the
        // following requests, so concurrent request might get a partially modified object, or worse, if an error
        // occurs during the save, the cached object will not reflect the actual document at all.
        doc = doc.clone();

        String language = form.getLanguage();
        // FIXME Which one should be used: doc.getDefaultLanguage or
        // form.getDefaultLanguage()?
        // String defaultLanguage = ((EditForm) form).getDefaultLanguage();
        XWikiDocument tdoc;

        if (doc.isNew() || (language == null) || (language.equals("")) || (language.equals("default"))
            || (language.equals(doc.getDefaultLanguage()))) {
            // Saving the default document translation.
            // Need to save parent and defaultLanguage if they have changed
            tdoc = doc;
        } else {
            tdoc = doc.getTranslatedDocument(language, context);
            if ((tdoc == doc) && xwiki.isMultiLingual(context)) {
                // Saving a new document translation.
                tdoc = new XWikiDocument(doc.getDocumentReference());
                tdoc.setLanguage(language);
                tdoc.setStore(doc.getStore());
                // In that specific case, we want the original doc to be the translation document so that we
                // never raised a conflict.
                originalDoc = tdoc;
            } else if (tdoc != doc) {
                // Saving an existing document translation (but not the default one).
                // Same as above, clone the object retrieved from the store cache.
                originalDoc = tdoc;
                tdoc = tdoc.clone();
            }
        }

        if (doc.isNew()) {
            doc.setLocale(Locale.ROOT);
            if (doc.getDefaultLocale() == Locale.ROOT) {
                doc.setDefaultLocale(xwiki.getLocalePreference(context));
            }
        }

        try {
            readFromTemplate(tdoc, form.getTemplate(), context);
        } catch (XWikiException e) {
            if (e.getCode() == XWikiException.ERROR_XWIKI_APP_DOCUMENT_NOT_EMPTY) {
                context.put("exception", e);
                return true;
            }
        }

        // Convert the content and the meta data of the edited document and its translations if the syntax has changed
        // and the request is asking for a syntax conversion. We do this after applying the template because the
        // template may have content in the previous syntax that needs to be converted. We do this before applying the
        // changes from the submitted form because it may contain content that was already converted.
        if (form.isConvertSyntax() && !tdoc.getSyntax().toIdString().equals(form.getSyntaxId())) {
            convertSyntax(tdoc, form.getSyntaxId(), context);
        }

        if (sectionNumber != 0) {
            XWikiDocument sectionDoc = tdoc.clone();
            sectionDoc.readFromForm(form, context);
            String sectionContent = sectionDoc.getContent() + "\n";
            String content = tdoc.updateDocumentSection(sectionNumber, sectionContent);
            tdoc.setContent(content);
            tdoc.setComment(sectionDoc.getComment());
            tdoc.setMinorEdit(sectionDoc.isMinorEdit());
        } else {
            tdoc.readFromForm(form, context);
        }

        // TODO: handle Author
        String username = context.getUser();
        tdoc.setAuthor(username);
        if (tdoc.isNew()) {
            tdoc.setCreator(username);
        }

        // Make sure we have at least the meta data dirty status
        tdoc.setMetaDataDirty(true);

        // Validate the document if we have xvalidate=1 in the request
        if ("1".equals(request.getParameter("xvalidate"))) {
            boolean validationResult = tdoc.validate(context);
            // If the validation fails we should show the "Inline form" edit mode
            if (validationResult == false) {
                // Set display context to 'edit'
                context.put("display", "edit");
                // Set the action used by the "Inline form" edit mode as the context action. See #render(XWikiContext).
                context.setAction(tdoc.getDefaultEditMode(context));
                // Set the document in the context
                context.put("doc", doc);
                context.put("cdoc", tdoc);
                context.put("tdoc", tdoc);
                // Force the "Inline form" edit mode.
                getCurrentScriptContext().setAttribute("editor", "inline", ScriptContext.ENGINE_SCOPE);

                return true;
            }
        }

        // Remove the redirect object if the save request doesn't update it. This allows users to easily overwrite
        // redirect place-holders that are created when we move pages around.
        if (tdoc.getXObject(RedirectClassDocumentInitializer.REFERENCE) != null
            && request.getParameter("XWiki.RedirectClass_0_location") == null) {
            tdoc.removeXObjects(RedirectClassDocumentInitializer.REFERENCE);
        }

        // We only proceed on the check between versions in case of AJAX request, so we currently stay in the edit form
        // This can be improved later by displaying a nice UI with some merge options in a sync request.
        // For now we don't want our user to loose their changes.
        if (isConflictCheckEnabled() && Utils.isAjaxRequest(context)
            && request.getParameter("previousVersion") != null) {
            if (isConflictingWithVersion(context, originalDoc, tdoc)) {
                return true;
            }
        }

        // Make sure the user is allowed to make this modification
        xwiki.checkSavingDocument(context.getUserReference(), tdoc, tdoc.getComment(), tdoc.isMinorEdit(), context);

        // We get the comment to be used from the document
        // It was read using readFromForm
        xwiki.saveDocument(tdoc, tdoc.getComment(), tdoc.isMinorEdit(), context);

        context.put(SAVED_OBJECT_VERSION_KEY, tdoc.getRCSVersion());

        Job createJob = startCreateJob(tdoc.getDocumentReference(), form);
        if (createJob != null) {
            if (isAsync(request)) {
                if (Utils.isAjaxRequest(context)) {
                    // Redirect to the job status URL of the job we have just launched.
                    sendRedirect(context.getResponse(), String.format("%s/rest/jobstatus/%s?media=json",
                        context.getRequest().getContextPath(), serializeJobId(createJob.getRequest().getId())));
                }

                // else redirect normally and the operation will eventually finish in the background.
                // Note: It is preferred that async mode is called in an AJAX request that can display the progress.
            } else {
                // Sync mode, default, wait for the work to finish.
                try {
                    createJob.join();
                } catch (InterruptedException e) {
                    throw new XWikiException(String.format(
                        "Interrupted while waiting for template [%s] to be processed when creating the document [%s]",
                        form.getTemplate(), tdoc.getDocumentReference()), e);
                }
            }
        } else {
            // Nothing more to do, just unlock the document.
            XWikiLock lock = tdoc.getLock(context);
            if (lock != null) {
                tdoc.removeLock(context);
            }
        }

        return false;
    }