Source: diff/delta/EditOperation.js

  1. import {DomHelper} from '../../util/DomHelper.js';
  2. import {Node} from '../../tree/Node.js';
  3. import xmldom from '@xmldom/xmldom';
  4. import {Dsl} from '../../config/Dsl.js';
  5. import {DiffConfig} from '../../config/DiffConfig.js';
  6. import vkbeautify from 'vkbeautify';
  7. /**
  8. * Data class for a single edit operation. Every edit operation in the CpeeDiff
  9. * change model is quadruple, although not all values must be present to
  10. * successfully apply an edit operation.
  11. *
  12. * @implements {XmlSerializable<EditOperation>}
  13. */
  14. export class EditOperation {
  15. /**
  16. * The type of edit operation.
  17. * @type {String}
  18. * @const
  19. */
  20. type;
  21. /**
  22. * The path of the node affected by this edit operation *before* it was
  23. * applied.
  24. * @type {?String}
  25. * @const
  26. */
  27. oldPath;
  28. /**
  29. * The path of the node affected by this edit operation *after* it was
  30. * applied.
  31. * @type {?String}
  32. * @const
  33. */
  34. newPath;
  35. /**
  36. * The new content added by this edit operation.
  37. * @type {?Node}
  38. * @const
  39. */
  40. newContent;
  41. /**
  42. * Construct a new EditOperation instance.
  43. * @param {String} type The type of edit operation.
  44. * @param {?String} oldPath The path of the node affected by this edit
  45. * operation *before* it was applied.
  46. * @param {?String} newPath The path of the node affected by this edit
  47. * operation *after* it was applied.
  48. * @param {?Node} newContent The new content added by this edit operation.
  49. */
  50. constructor(
  51. type,
  52. oldPath = null,
  53. newPath = null,
  54. newContent = null,
  55. ) {
  56. this.type = type;
  57. this.oldPath = oldPath;
  58. this.newPath = newPath;
  59. this.newContent = newContent;
  60. }
  61. /**
  62. * @param {Object} xmlElement The XML DOM object.
  63. * @return {EditOperation}
  64. */
  65. static fromXmlDom(xmlElement) {
  66. let newContent;
  67. const xmlContent = DomHelper.firstChildElement(xmlElement);
  68. if (xmlContent != null) {
  69. newContent = Node.fromXmlDom(xmlContent);
  70. }
  71. return new EditOperation(
  72. xmlElement.localName,
  73. xmlElement.getAttribute('oldPath').slice(1), // Drop root slash
  74. xmlElement.getAttribute('newPath').slice(1),
  75. newContent,
  76. );
  77. }
  78. /**
  79. * @param {String} xml The XML document.
  80. * @return {EditOperation}
  81. */
  82. static fromXmlString(xml) {
  83. return this.fromXmlDom(DomHelper.firstChildElement(
  84. new xmldom
  85. .DOMParser()
  86. .parseFromString(xml, 'text/xml')));
  87. }
  88. /** @return {Boolean} */
  89. isDeletion() {
  90. return this.type === Dsl.CHANGE_MODEL.DELETION.label;
  91. }
  92. /** @return {Boolean} */
  93. isInsertion() {
  94. return this.type === Dsl.CHANGE_MODEL.INSERTION.label;
  95. }
  96. /** @return {Boolean} */
  97. isMove() {
  98. return this.type === Dsl.CHANGE_MODEL.MOVE.label;
  99. }
  100. /** @return {Boolean} */
  101. isUpdate() {
  102. return this.type === Dsl.CHANGE_MODEL.UPDATE.label;
  103. }
  104. /**
  105. * @return {String} A string representation of this edit operation.
  106. */
  107. toString() {
  108. return this.type + ' ' +
  109. (this.oldPath !== null ? this.oldPath + ' ' : '') +
  110. (this.oldPath !== null && this.newPath !== null ? '-> ' : '') +
  111. (this.newPath !== null ? this.newPath + ' ' : '') +
  112. (this.newContent !== null ? this.newContent + ' ' : '');
  113. }
  114. /**
  115. * @param {Object} ownerDocument The owner document of the generated XML
  116. * element.
  117. * @return {Object} The XML DOM object for this edit operation.
  118. */
  119. toXmlDom(ownerDocument = xmldom
  120. .DOMImplementation
  121. .prototype
  122. .createDocument(Dsl.DEFAULT_NAMESPACE)) {
  123. const xmlNode = ownerDocument.createElement(this.type);
  124. if (this.oldPath != null) {
  125. // Add root slash
  126. xmlNode.setAttribute('oldPath', '/' + this.oldPath);
  127. }
  128. if (this.newPath != null) {
  129. // Add root slash
  130. xmlNode.setAttribute('newPath', '/' + this.newPath);
  131. }
  132. if (this.newContent != null) {
  133. xmlNode.appendChild(this.newContent.toXmlDom(ownerDocument));
  134. }
  135. return xmlNode;
  136. }
  137. /**
  138. * @return {String} The XML document for this edit operation.
  139. */
  140. toXmlString() {
  141. const str = new xmldom.XMLSerializer().serializeToString(this.toXmlDom());
  142. if (DiffConfig.PRETTY_XML) {
  143. return vkbeautify.xml(str);
  144. } else {
  145. return str;
  146. }
  147. }
  148. }