private static void addNewLineToTag(CompositeElement tag, Project project) {
    LOG.assertTrue(tag != null && tag.getElementType() == DOC_TAG);
    ASTNode current = tag.getLastChildNode();
    while (current != null
        && current.getElementType() == DOC_COMMENT_DATA
        && isWhitespaceCommentData(current)) {
      current = current.getTreePrev();
    }
    if (current != null && current.getElementType() == DOC_COMMENT_LEADING_ASTERISKS) return;
    final CharTable treeCharTab = SharedImplUtil.findCharTableByTree(tag);
    final ASTNode newLine =
        Factory.createSingleLeafElement(
            DOC_COMMENT_DATA, "\n", 0, 1, treeCharTab, SharedImplUtil.getManagerByTree(tag));
    tag.addChild(newLine, null);

    ASTNode leadingWhitespaceAnchor = null;
    if (JavaCodeStyleSettingsFacade.getInstance(project).isJavaDocLeadingAsterisksEnabled()) {
      final TreeElement leadingAsterisk =
          Factory.createSingleLeafElement(
              DOC_COMMENT_LEADING_ASTERISKS,
              "*",
              0,
              1,
              treeCharTab,
              SharedImplUtil.getManagerByTree(tag));

      leadingWhitespaceAnchor =
          tag.addInternal(leadingAsterisk, leadingAsterisk, null, Boolean.TRUE);
    }

    final TreeElement commentData =
        Factory.createSingleLeafElement(
            DOC_COMMENT_DATA, " ", 0, 1, treeCharTab, SharedImplUtil.getManagerByTree(tag));
    tag.addInternal(commentData, commentData, leadingWhitespaceAnchor, Boolean.TRUE);
  }
  public static void replaceWhiteSpace(
      final String whiteSpace,
      final ASTNode leafElement,
      final IElementType whiteSpaceToken,
      @Nullable final TextRange textRange) {
    final CharTable charTable = SharedImplUtil.findCharTableByTree(leafElement);

    ASTNode treePrev = findPreviousWhiteSpace(leafElement, whiteSpaceToken);
    if (treePrev == null) {
      treePrev = getWsCandidate(leafElement);
    }

    if (treePrev != null
        && treePrev.getText().trim().isEmpty()
        && treePrev.getElementType() != whiteSpaceToken
        && treePrev.getTextLength() > 0
        && !whiteSpace.isEmpty()) {
      LeafElement whiteSpaceElement =
          Factory.createSingleLeafElement(
              treePrev.getElementType(),
              whiteSpace,
              charTable,
              SharedImplUtil.getManagerByTree(leafElement));

      ASTNode treeParent = treePrev.getTreeParent();
      treeParent.replaceChild(treePrev, whiteSpaceElement);
    } else {
      LeafElement whiteSpaceElement =
          Factory.createSingleLeafElement(
              whiteSpaceToken, whiteSpace, charTable, SharedImplUtil.getManagerByTree(leafElement));

      if (treePrev == null) {
        if (!whiteSpace.isEmpty()) {
          addWhiteSpace(leafElement, whiteSpaceElement);
        }
      } else {
        if (!(treePrev.getElementType() == whiteSpaceToken)) {
          if (!whiteSpace.isEmpty()) {
            addWhiteSpace(treePrev, whiteSpaceElement);
          }
        } else {
          if (treePrev.getElementType() == whiteSpaceToken) {
            final CompositeElement treeParent = (CompositeElement) treePrev.getTreeParent();
            if (!whiteSpace.isEmpty()) {
              //          LOG.assertTrue(textRange == null ||
              // treeParent.getTextRange().equals(textRange));
              treeParent.replaceChild(treePrev, whiteSpaceElement);
            } else {
              treeParent.removeChild(treePrev);
            }

            // There is a possible case that more than one PSI element is matched by the target text
            // range.
            // That is the case, for example, for Python's multi-line expression. It may looks like
            // below:
            //     import contextlib,\
            //       math, decimal
            // Here single range contains two blocks: '\' & '\n  '. So, we may want to replace that
            // range to another text, hence,
            // we replace last element located there with it ('\n  ') and want to remove any
            // remaining elements ('\').
            ASTNode removeCandidate = findPreviousWhiteSpace(whiteSpaceElement, whiteSpaceToken);
            while (textRange != null
                && removeCandidate != null
                && removeCandidate.getStartOffset() >= textRange.getStartOffset()) {
              treePrev = findPreviousWhiteSpace(removeCandidate, whiteSpaceToken);
              removeCandidate.getTreeParent().removeChild(removeCandidate);
              removeCandidate = treePrev;
            }
            // treeParent.subtreeChanged();
          }
        }
      }
    }
  }