Source: eval/diff_adapters/XccAdapter.js

import {EvalConfig} from '../../config/EvalConfig.js';
import {DiffAdapter} from './DiffAdapter.js';
import {DomHelper} from '../../util/DomHelper.js';
import xmldom from '@xmldom/xmldom';
import {Node} from '../../tree/Node.js';

/**
 * Adapter class for the 'XML Change Control' algorithm by S. Rönnau and U. M.
 * Borghoff.
 *
 * @see https://launchpad.net/xcc
 */
export class XccAdapter extends DiffAdapter {

  /**
   * Construct a new XccAdapter instance
   */
  constructor() {
    super(EvalConfig.DIFFS.XCC.path, EvalConfig.DIFFS.XCC.displayName);
  }

  /**
   * @inheritDoc
   * @override
   */
  parseOutput(output) {
    let updates = 0;
    let insertions = 0;
    let moves = 0;
    let deletions = 0;
    let cost = 0;

    // Enclosing tag is 'delta'
    const delta = DomHelper.firstChildElement(
        new xmldom.DOMParser().parseFromString(output, 'text/xml'),
        'delta',
    );
    DomHelper.forAllChildElements(delta, (xmlOperation) => {
      switch (xmlOperation.localName) {
        case 'insert':
          // Moves are insertions and deletions that are linked by an 'id'
          // attribute
          if (xmlOperation.hasAttribute('id')) {
            moves++;
          } else {
            // Determine cost
            const xmlNewValue = DomHelper.firstChildElement(
                xmlOperation,
                'newvalue',
            );
            if (DomHelper.firstChildElement(xmlNewValue) == null) {
              updates++;
            } else {
              insertions++;
              DomHelper.forAllChildElements(xmlNewValue, (xmlElement) => {
                cost += Node.fromXmlDom(xmlElement).size();
              });
            }
          }
          break;
        case 'delete':
          // Moves are accounted for in the insertion case
          if (!xmlOperation.hasAttribute('id')) {
            // Determine cost
            const xmlOldValue = DomHelper.firstChildElement(
                xmlOperation,
                'oldvalue',
            );
            if (DomHelper.firstChildElement(xmlOldValue) == null) {
              updates++;
            } else {
              deletions++;
              DomHelper.forAllChildElements(xmlOldValue, (xmlElement) => {
                cost += Node.fromXmlDom(xmlElement).size();
              });
            }
          }
          break;
        case 'update':
          updates++;
          break;
        default: {
          console.log('other');
        }
      }
    });
    // Unit cost
    cost += updates + moves;
    return [
      insertions,
      moves,
      updates,
      deletions,
      cost,
    ];
  }
}