Source: merge/MergeNode.js

import {DeltaNode} from '../diff/patch/DeltaNode.js';
import {Confidence} from './Confidence.js';
import {Dsl} from '../config/Dsl.js';
import xmldom from '@xmldom/xmldom';

/**
 * A node inside a merged process tree.
 *
 * @implements {XmlSerializable<MergeNode>}
 * @extends {DeltaNode}
 */
export class MergeNode extends DeltaNode {
  /**
   * The branch in which this node was changed (none: 0, branch 1: 1,
   * branch 2: 2, or both: 3).
   * @type {Number}
   */
  changeOrigin;
  /**
   * An object containing the confidence of the merge regarding
   * the node's content, parent node, and position (within its parent's child
   * list).
   * @type {Confidence}
   */
  confidence;

  /**
   * Construct a new MergeNode instance.
   * @param {String} label The node label.
   * @param {?String} text The text content.
   * @param {String} type The type of change this node was affected by.
   * @param {?Number} baseNode The base node ID.
   * @param {Number} changeOrigin The branch in which this node was changed (0
   *     if unchanged).
   */
  constructor(
      label,
      text = null,
      type = 'NIL',
      baseNode = null,
      changeOrigin = 0,
  ) {
    super(label, text, type, baseNode);
    this.changeOrigin = changeOrigin;
    // Initial confidence is high
    this.confidence = new Confidence(true, true, true);
  }

  /**
   * Create a new MergeNode instance from an existing node.
   * @param {Node} node The existing node.
   * @param {Boolean} includeChildren Whether to copy the children of the node.
   * @return {MergeNode}
   * @override
   */
  static fromNode(node, includeChildren = true) {
    const mergeNode = new MergeNode(node.label, node.text);
    for (const [key, value] of node.attributes) {
      mergeNode.attributes.set(key, value);
    }
    if (includeChildren) {
      for (const child of node) {
        mergeNode.appendChild(this.fromNode(child, includeChildren));
      }
    }
    if (node instanceof DeltaNode) {
      mergeNode.type = node.type;
      mergeNode.baseNode = node.baseNode;
      for (const placeholder of node.placeholders) {
        mergeNode.placeholders.push(this.fromNode(placeholder, true));
      }
      for (const [key, update] of node.updates) {
        mergeNode.updates.set(key, update.copy());
      }
    }
    if (node instanceof MergeNode) {
      mergeNode.confidence = Object.assign(new Confidence(), node.confidence);
      mergeNode.changeOrigin = node.changeOrigin;
    }
    return mergeNode;
  }

  /**
   * @return {Object} XML DOM object for this merge node and its children.
   * @override
   */
  toXmlDom(ownerDocument = xmldom
      .DOMImplementation
      .prototype
      .createDocument(Dsl.DEFAULT_NAMESPACE)) {
    const deltaXmlRoot = DeltaNode.fromNode(this, true).toXmlDom(ownerDocument);

    deltaXmlRoot.setAttribute('xmlns:' + Dsl.MERGE_TREE.NAMESPACE_PREFIX,
        Dsl.MERGE_TREE.NAMESPACE_URI);

    const annotate = (mergeNode, xmlElement) => {
      if (!mergeNode.isUnchanged()) {
        for (const [key, update] of mergeNode.updates) {
          xmlElement.setAttribute(
              Dsl.MERGE_TREE.NAMESPACE_PREFIX + ':' + key,
              update.origin,
          );
        }
        if (mergeNode.isDeleted() ||
            mergeNode.isMoved() ||
            mergeNode.isInserted()) {
          xmlElement.setAttribute(
              Dsl.MERGE_TREE.NAMESPACE_PREFIX + ':this',
              mergeNode.changeOrigin,
          );
        }
        if (!mergeNode.confidence.contentConfident) {
          xmlElement.setAttribute(
              Dsl.MERGE_TREE.NAMESPACE_PREFIX + ':content_confident',
              'false',
          );
        }
        if (!mergeNode.confidence.parentConfident) {
          xmlElement.setAttribute(
              Dsl.MERGE_TREE.NAMESPACE_PREFIX + ':parent_confident',
              'false',
          );
        }
        if (!mergeNode.confidence.positionConfident) {
          xmlElement.setAttribute(
              Dsl.MERGE_TREE.NAMESPACE_PREFIX + ':position_confident',
              'false',
          );
        }
      }
      for (let i = 0; i < mergeNode.degree(); i++) {
        annotate(mergeNode.getChild(i), xmlElement.childNodes.item(i));
      }
    };

    annotate(this, deltaXmlRoot);
    return deltaXmlRoot;
  }
}