private void renameLinks(
      XWikiDocument document, DocumentReference oldTarget, DocumentReference newTarget)
      throws XWikiException {
    DocumentReference currentDocumentReference = document.getDocumentReference();

    // We support only the syntaxes for which there is an available renderer.
    if (!this.contextComponentManagerProvider
        .get()
        .hasComponent(BlockRenderer.class, document.getSyntax().toIdString())) {
      this.logger.warn(
          "We can't rename the links from [{}] "
              + "because there is no renderer available for its syntax [{}].",
          currentDocumentReference,
          document.getSyntax());
      return;
    }

    XDOM xdom = document.getXDOM();
    List<Block> blocks = linkedResourceHelper.getBlocks(xdom);

    boolean modified = false;
    for (Block block : blocks) {
      try {
        modified |= renameLink(block, currentDocumentReference, oldTarget, newTarget);
      } catch (InvalidArgumentException e) {
        continue;
      }
    }

    if (modified) {
      document.setContent(xdom);
      saveDocumentPreservingContentAuthor(document, "Renamed back-links.", false);
      this.logger.info(
          "The links from [{}] that were targeting [{}] have been updated to target [{}].",
          document.getDocumentReferenceWithLocale(),
          oldTarget,
          newTarget);
    } else {
      this.logger.info("No back-links to update in [{}].", currentDocumentReference);
    }
  }
  private void updateRelativeLinks(XWikiDocument document, DocumentReference oldDocumentReference)
      throws XWikiException {
    // We support only the syntaxes for which there is an available renderer.
    if (!this.contextComponentManagerProvider
        .get()
        .hasComponent(BlockRenderer.class, document.getSyntax().toIdString())) {
      this.logger.warn(
          "We can't update the relative links from [{}]"
              + " because there is no renderer available for its syntax [{}].",
          document.getDocumentReference(),
          document.getSyntax());
      return;
    }

    DocumentReference newDocumentReference = document.getDocumentReference();

    XDOM xdom = document.getXDOM();
    List<Block> blocks = linkedResourceHelper.getBlocks(xdom);

    boolean modified = false;
    for (Block block : blocks) {
      ResourceReference resourceReference = linkedResourceHelper.getResourceReference(block);
      if (resourceReference == null) {
        // Skip invalid blocks.
        continue;
      }

      ResourceType resourceType = resourceReference.getType();

      // TODO: support ATTACHMENT as well.
      if (!ResourceType.DOCUMENT.equals(resourceType) && !ResourceType.SPACE.equals(resourceType)) {
        // We are currently only interested in Document or Space references.
        continue;
      }

      // current link, use the old document's reference to fill in blanks.
      EntityReference oldLinkReference =
          this.resourceReferenceResolver.resolve(resourceReference, null, oldDocumentReference);
      // new link, use the new document's reference to fill in blanks.
      EntityReference newLinkReference =
          this.resourceReferenceResolver.resolve(resourceReference, null, newDocumentReference);

      // If the new and old link references don`t match, then we must update the relative link.
      if (!newLinkReference.equals(oldLinkReference)) {
        modified = true;

        // Serialize the old (original) link relative to the new document's location, in compact
        // form.
        String serializedLinkReference =
            this.compactEntityReferenceSerializer.serialize(oldLinkReference, newDocumentReference);

        // Update the reference in the XDOM.
        linkedResourceHelper.setResourceReferenceString(block, serializedLinkReference);
      }
    }

    if (modified) {
      document.setContent(xdom);
      saveDocumentPreservingContentAuthor(document, "Updated the relative links.", true);
      this.logger.info("Updated the relative links from [{}].", document.getDocumentReference());
    } else {
      this.logger.info("No relative links to update in [{}].", document.getDocumentReference());
    }
  }
  /**
   * Creates a {@link WikiMacro} from an {@link XWikiDocument} which contains a macro definition.
   *
   * @param doc the {@link XWikiDocument} to look for a macro definition
   * @return the {@link WikiMacro} found inside the document
   * @throws WikiMacroException when an invalid macro definition or no macro definition was found
   */
  private WikiMacro buildMacro(XWikiDocument doc) throws WikiMacroException {
    DocumentReference documentReference = doc.getDocumentReference();

    // Check whether this document contains a macro definition.
    BaseObject macroDefinition = doc.getObject(WIKI_MACRO_CLASS);
    if (null == macroDefinition) {
      throw new WikiMacroException(
          String.format("No macro definition found in document : [%s]", documentReference));
    }

    // Extract macro definition.
    String macroId = macroDefinition.getStringValue(MACRO_ID_PROPERTY);
    String macroName = macroDefinition.getStringValue(MACRO_NAME_PROPERTY);
    // The macro description as plain text
    String macroDescription = macroDefinition.getStringValue(MACRO_DESCRIPTION_PROPERTY);
    String macroDefaultCategory = macroDefinition.getStringValue(MACRO_DEFAULT_CATEGORY_PROPERTY);
    WikiMacroVisibility macroVisibility =
        WikiMacroVisibility.fromString(macroDefinition.getStringValue(MACRO_VISIBILITY_PROPERTY));
    boolean macroSupportsInlineMode =
        (macroDefinition.getIntValue(MACRO_INLINE_PROPERTY) == 0) ? false : true;
    String macroContentType = macroDefinition.getStringValue(MACRO_CONTENT_TYPE_PROPERTY);
    // The macro content description as plain text
    String macroContentDescription =
        macroDefinition.getStringValue(MACRO_CONTENT_DESCRIPTION_PROPERTY);
    String macroCode = macroDefinition.getStringValue(MACRO_CODE_PROPERTY);

    // Verify macro id.
    if (StringUtils.isEmpty(macroId)) {
      throw new WikiMacroException(
          String.format(
              "Incomplete macro definition in [%s], macro id is empty", documentReference));
    }

    // Verify macro name.
    if (StringUtils.isEmpty(macroName)) {
      macroName = macroId;
      this.logger.debug(
          String.format(
              "Incomplete macro definition in [%s], macro name is empty", documentReference));
    }

    // Verify macro description.
    if (StringUtils.isEmpty(macroDescription)) {
      this.logger.debug(
          String.format(
              "Incomplete macro definition in [%s], macro description is empty",
              documentReference));
    }

    // Verify default macro category.
    if (StringUtils.isEmpty(macroDefaultCategory)) {
      macroDefaultCategory = null;
      this.logger.debug(
          String.format(
              "Incomplete macro definition in [%s], default macro category is empty",
              documentReference));
    }

    // Verify macro content type.
    if (StringUtils.isEmpty(macroContentType)) {
      macroContentType = MACRO_CONTENT_OPTIONAL;
    }

    // Verify macro content description.
    if (!macroContentType.equals(MACRO_CONTENT_EMPTY)
        && StringUtils.isEmpty(macroContentDescription)) {
      String errorMsg = "Incomplete macro definition in [%s], macro content description is empty";
      this.logger.debug(String.format(errorMsg, documentReference));
      macroContentDescription = "Macro content";
    }

    // Verify macro code.
    if (StringUtils.isEmpty(macroCode)) {
      throw new WikiMacroException(
          String.format(
              "Incomplete macro definition in [%s], macro code is empty", documentReference));
    }

    // Extract macro parameters.
    List<WikiMacroParameterDescriptor> parameterDescriptors =
        new ArrayList<WikiMacroParameterDescriptor>();
    Vector<BaseObject> macroParameters = doc.getObjects(WIKI_MACRO_PARAMETER_CLASS);
    if (null != macroParameters) {
      for (BaseObject macroParameter : macroParameters) {
        // Vectors can contain null values
        if (null == macroParameter) {
          continue;
        }

        // Extract parameter definition.
        String parameterName = macroParameter.getStringValue(PARAMETER_NAME_PROPERTY);
        String parameterDescription = macroParameter.getStringValue(PARAMETER_DESCRIPTION_PROPERTY);
        boolean parameterMandatory =
            (macroParameter.getIntValue(PARAMETER_MANDATORY_PROPERTY) == 0) ? false : true;
        String parameterDefaultValue =
            macroParameter.getStringValue(PARAMETER_DEFAULT_VALUE_PROPERTY);

        // Verify parameter name.
        if (StringUtils.isEmpty(parameterName)) {
          throw new WikiMacroException(
              String.format(
                  "Incomplete macro definition in [%s], macro parameter name is empty",
                  documentReference));
        }

        // Verify parameter description.
        if (StringUtils.isEmpty(parameterDescription)) {
          String errorMessage =
              "Incomplete macro definition in [%s], macro parameter description is empty";
          this.logger.debug(String.format(errorMessage, documentReference));
        }

        // If field empty, assume no default value was provided.
        if (StringUtils.isEmpty(parameterDefaultValue)) {
          parameterDefaultValue = null;
        }

        // Create the parameter descriptor.
        parameterDescriptors.add(
            new WikiMacroParameterDescriptor(
                parameterName, parameterDescription, parameterMandatory, parameterDefaultValue));
      }
    }

    // Create macro content descriptor.
    ContentDescriptor contentDescriptor = null;
    if (!macroContentType.equals(MACRO_CONTENT_EMPTY)) {
      contentDescriptor =
          new DefaultContentDescriptor(
              macroContentDescription, macroContentType.equals(MACRO_CONTENT_MANDATORY));
    }

    // Create macro descriptor.
    MacroId id = new MacroId(macroId, doc.getSyntax());
    MacroDescriptor macroDescriptor =
        new WikiMacroDescriptor(
            id,
            macroName,
            macroDescription,
            macroDefaultCategory,
            macroVisibility,
            contentDescriptor,
            parameterDescriptors);

    XDOM xdom;
    try {
      xdom = parser.parse(macroCode, doc.getSyntax(), documentReference);
    } catch (MissingParserException ex) {
      throw new WikiMacroException("Could not find a parser for macro content", ex);
    } catch (ParseException ex) {
      throw new WikiMacroException("Error while parsing macro content", ex);
    }

    // Create & return the macro.
    return new DefaultWikiMacro(
        documentReference,
        doc.getAuthorReference(),
        macroSupportsInlineMode,
        macroDescriptor,
        xdom,
        doc.getSyntax(),
        this.componentManager);
  }