xml-stream.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. "use strict";
  2. const _ = require('./under-dash');
  3. const utils = require('./utils');
  4. // constants
  5. const OPEN_ANGLE = '<';
  6. const CLOSE_ANGLE = '>';
  7. const OPEN_ANGLE_SLASH = '</';
  8. const CLOSE_SLASH_ANGLE = '/>';
  9. function pushAttribute(xml, name, value) {
  10. xml.push(` ${name}="${utils.xmlEncode(value.toString())}"`);
  11. }
  12. function pushAttributes(xml, attributes) {
  13. if (attributes) {
  14. const tmp = [];
  15. _.each(attributes, (value, name) => {
  16. if (value !== undefined) {
  17. pushAttribute(tmp, name, value);
  18. }
  19. });
  20. xml.push(tmp.join(""));
  21. }
  22. }
  23. class XmlStream {
  24. constructor() {
  25. this._xml = [];
  26. this._stack = [];
  27. this._rollbacks = [];
  28. }
  29. get tos() {
  30. return this._stack.length ? this._stack[this._stack.length - 1] : undefined;
  31. }
  32. get cursor() {
  33. // handy way to track whether anything has been added
  34. return this._xml.length;
  35. }
  36. openXml(docAttributes) {
  37. const xml = this._xml;
  38. // <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  39. xml.push('<?xml');
  40. pushAttributes(xml, docAttributes);
  41. xml.push('?>\n');
  42. }
  43. openNode(name, attributes) {
  44. const parent = this.tos;
  45. const xml = this._xml;
  46. if (parent && this.open) {
  47. xml.push(CLOSE_ANGLE);
  48. }
  49. this._stack.push(name);
  50. // start streaming node
  51. xml.push(OPEN_ANGLE);
  52. xml.push(name);
  53. pushAttributes(xml, attributes);
  54. this.leaf = true;
  55. this.open = true;
  56. }
  57. addAttribute(name, value) {
  58. if (!this.open) {
  59. throw new Error('Cannot write attributes to node if it is not open');
  60. }
  61. if (value !== undefined) {
  62. pushAttribute(this._xml, name, value);
  63. }
  64. }
  65. addAttributes(attrs) {
  66. if (!this.open) {
  67. throw new Error('Cannot write attributes to node if it is not open');
  68. }
  69. pushAttributes(this._xml, attrs);
  70. }
  71. writeText(text) {
  72. const xml = this._xml;
  73. if (this.open) {
  74. xml.push(CLOSE_ANGLE);
  75. this.open = false;
  76. }
  77. this.leaf = false;
  78. xml.push(utils.xmlEncode(text.toString()));
  79. }
  80. writeXml(xml) {
  81. if (this.open) {
  82. this._xml.push(CLOSE_ANGLE);
  83. this.open = false;
  84. }
  85. this.leaf = false;
  86. this._xml.push(xml);
  87. }
  88. closeNode() {
  89. const node = this._stack.pop();
  90. const xml = this._xml;
  91. if (this.leaf) {
  92. xml.push(CLOSE_SLASH_ANGLE);
  93. } else {
  94. xml.push(OPEN_ANGLE_SLASH);
  95. xml.push(node);
  96. xml.push(CLOSE_ANGLE);
  97. }
  98. this.open = false;
  99. this.leaf = false;
  100. }
  101. leafNode(name, attributes, text) {
  102. this.openNode(name, attributes);
  103. if (text !== undefined) {
  104. // zeros need to be written
  105. this.writeText(text);
  106. }
  107. this.closeNode();
  108. }
  109. closeAll() {
  110. while (this._stack.length) {
  111. this.closeNode();
  112. }
  113. }
  114. addRollback() {
  115. this._rollbacks.push({
  116. xml: this._xml.length,
  117. stack: this._stack.length,
  118. leaf: this.leaf,
  119. open: this.open
  120. });
  121. return this.cursor;
  122. }
  123. commit() {
  124. this._rollbacks.pop();
  125. }
  126. rollback() {
  127. const r = this._rollbacks.pop();
  128. if (this._xml.length > r.xml) {
  129. this._xml.splice(r.xml, this._xml.length - r.xml);
  130. }
  131. if (this._stack.length > r.stack) {
  132. this._stack.splice(r.stack, this._stack.length - r.stack);
  133. }
  134. this.leaf = r.leaf;
  135. this.open = r.open;
  136. }
  137. get xml() {
  138. this.closeAll();
  139. return this._xml.join('');
  140. }
  141. }
  142. XmlStream.StdDocAttributes = {
  143. version: '1.0',
  144. encoding: 'UTF-8',
  145. standalone: 'yes'
  146. };
  147. module.exports = XmlStream;
  148. //# sourceMappingURL=xml-stream.js.map