public static @NotNull Element deepMergeWithAttributes(@NotNull Element to, @NotNull Element from, @NotNull List<MergeAttribute> mergeByAttributes) {
    for (Iterator<Element> iterator = from.getChildren().iterator(); iterator.hasNext(); ) {
      Element child = iterator.next();
      iterator.remove();

      Element existingChild = to.getChild(child.getName());
      if (existingChild != null && isEmpty(existingChild)) {
        // replace empty tag
        to.removeChild(child.getName());
        existingChild = null;
      }

      // if no children (e.g. `<module fileurl="value" />`), it means that element should be added as list item
      if (existingChild == null ||
          existingChild.getChildren().isEmpty() ||
          !areAttributesEqual(getAttributes(existingChild), getAttributes(child), existingChild, mergeByAttributes)) {
        to.addContent(child);
      }
      else {
        deepMergeWithAttributes(existingChild, child, mergeByAttributes);
      }
    }

    for (Iterator<Attribute> iterator = getAttributes(from).iterator(); iterator.hasNext(); ) {
      Attribute attribute = iterator.next();
      iterator.remove();
      to.setAttribute(attribute);
    }
    return to;
  }
public static @NotNull Element deepMergeWithAttributes(@NotNull Element to, @NotNull Element from, @NotNull List<MergeAttribute> mergeByAttributes)