UNPKG

10.6 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3/**
4 * Copyright (C) 2016-2019 Michael Kourlas
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18var xmlcreate_1 = require("xmlcreate");
19var options_1 = require("./options");
20var utils_1 = require("./utils");
21/**
22 * Indicates that an object of a particular type should be suppressed from the
23 * XML output.
24 *
25 * See the `typeHandlers` property in {@link IOptions} for more details.
26 */
27var Absent = /** @class */ (function () {
28 function Absent() {
29 }
30 Object.defineProperty(Absent, "instance", {
31 /**
32 * Returns the sole instance of Absent.
33 */
34 get: function () {
35 return Absent._instance;
36 },
37 enumerable: true,
38 configurable: true
39 });
40 Absent._instance = new Absent();
41 return Absent;
42}());
43exports.Absent = Absent;
44/**
45 * Gets the type handler associated with a value.
46 *
47 * @private
48 */
49function getHandler(value, options) {
50 var type = Object.prototype.toString.call(value);
51 var handler;
52 if (Object.prototype.hasOwnProperty.call(options.typeHandlers, "*")) {
53 handler = options.typeHandlers["*"];
54 }
55 if (Object.prototype.hasOwnProperty.call(options.typeHandlers, type)) {
56 handler = options.typeHandlers[type];
57 }
58 return handler;
59}
60/**
61 * Parses a string into XML and adds it to the parent element or attribute.
62 *
63 * @private
64 */
65function parseString(str, parentElement, options) {
66 var requiresCdata = function (s) {
67 return (options.cdataInvalidChars && (s.indexOf("<") !== -1
68 || s.indexOf("&") !== -1))
69 || options.cdataKeys.indexOf(parentElement.name) !== -1
70 || options.cdataKeys.indexOf("*") !== -1;
71 };
72 if (parentElement instanceof xmlcreate_1.XmlElement) {
73 if (requiresCdata(str)) {
74 var cdataStrs = str.split("]]>");
75 for (var i = 0; i < cdataStrs.length; i++) {
76 if (requiresCdata(cdataStrs[i])) {
77 parentElement.cdata({
78 charData: cdataStrs[i],
79 replaceInvalidCharsInCharData: options.replaceInvalidChars
80 });
81 }
82 else {
83 parentElement.charData({
84 charData: cdataStrs[i],
85 replaceInvalidCharsInCharData: options.replaceInvalidChars
86 });
87 }
88 if (i < cdataStrs.length - 1) {
89 parentElement.charData({
90 charData: "]]>",
91 replaceInvalidCharsInCharData: options.replaceInvalidChars
92 });
93 }
94 }
95 }
96 else {
97 parentElement.charData({
98 charData: str,
99 replaceInvalidCharsInCharData: options.replaceInvalidChars
100 });
101 }
102 }
103 else {
104 parentElement.text({
105 charData: str,
106 replaceInvalidCharsInCharData: options.replaceInvalidChars
107 });
108 }
109}
110/**
111 * Parses an attribute into XML and adds it to the parent element.
112 *
113 * @private
114 */
115function parseAttribute(name, value, parentElement, options) {
116 var attribute = parentElement.attribute({
117 name: name,
118 replaceInvalidCharsInName: options.replaceInvalidChars
119 });
120 parseString(utils_1.stringify(value), attribute, options);
121}
122/**
123 * Parses an object or Map entry into XML and adds it to the parent element.
124 *
125 * @private
126 */
127function parseObjectOrMapEntry(key, value, parentElement, options) {
128 // Alias key
129 if (key === options.aliasString) {
130 parentElement.name = utils_1.stringify(value);
131 return;
132 }
133 // Attributes key
134 if (key.indexOf(options.attributeString) === 0 && utils_1.isObject(value)) {
135 for (var _i = 0, _a = Object.keys(value); _i < _a.length; _i++) {
136 var subkey = _a[_i];
137 parseAttribute(subkey, utils_1.stringify(value[subkey]), parentElement, options);
138 }
139 return;
140 }
141 // Value key
142 if (key.indexOf(options.valueString) === 0) {
143 parseValue(key, utils_1.stringify(value), parentElement, options);
144 return;
145 }
146 // Standard handling (create new element for entry)
147 var element = parentElement;
148 if (!utils_1.isArray(value) && !utils_1.isSet(value)) {
149 // If handler for value returns absent, then do not add element
150 var handler = getHandler(value, options);
151 if (!utils_1.isUndefined(handler)) {
152 if (handler(value) === Absent.instance) {
153 return;
154 }
155 }
156 element = parentElement.element({
157 name: key,
158 replaceInvalidCharsInName: options.replaceInvalidChars,
159 useSelfClosingTagIfEmpty: options.useSelfClosingTagIfEmpty
160 });
161 }
162 parseValue(key, value, element, options);
163}
164/**
165 * Parses an Object or Map into XML and adds it to the parent element.
166 *
167 * @private
168 */
169function parseObjectOrMap(objectOrMap, parentElement, options) {
170 if (utils_1.isMap(objectOrMap)) {
171 objectOrMap.forEach(function (value, key) {
172 parseObjectOrMapEntry(utils_1.stringify(key), value, parentElement, options);
173 });
174 }
175 else {
176 for (var _i = 0, _a = Object.keys(objectOrMap); _i < _a.length; _i++) {
177 var key = _a[_i];
178 parseObjectOrMapEntry(key, objectOrMap[key], parentElement, options);
179 }
180 }
181}
182/**
183 * Parses an array or Set into XML and adds it to the parent element.
184 *
185 * @private
186 */
187function parseArrayOrSet(key, arrayOrSet, parentElement, options) {
188 var arrayNameFunc;
189 if (Object.prototype.hasOwnProperty.call(options.wrapHandlers, "*")) {
190 arrayNameFunc = options.wrapHandlers["*"];
191 }
192 if (Object.prototype.hasOwnProperty.call(options.wrapHandlers, key)) {
193 arrayNameFunc = options.wrapHandlers[key];
194 }
195 var arrayKey = key;
196 var arrayElement = parentElement;
197 if (!utils_1.isUndefined(arrayNameFunc)) {
198 var arrayNameFuncKey = arrayNameFunc(arrayKey, arrayOrSet);
199 if (!utils_1.isNull(arrayNameFuncKey)) {
200 arrayKey = arrayNameFuncKey;
201 arrayElement = parentElement.element({
202 name: key,
203 replaceInvalidCharsInName: options.replaceInvalidChars,
204 useSelfClosingTagIfEmpty: options.useSelfClosingTagIfEmpty
205 });
206 }
207 }
208 arrayOrSet.forEach(function (item) {
209 var element = arrayElement;
210 if (!utils_1.isArray(item) && !utils_1.isSet(item)) {
211 // If handler for value returns absent, then do not add element
212 var handler = getHandler(item, options);
213 if (!utils_1.isUndefined(handler)) {
214 if (handler(item) === Absent.instance) {
215 return;
216 }
217 }
218 element = arrayElement.element({
219 name: arrayKey,
220 replaceInvalidCharsInName: options.replaceInvalidChars,
221 useSelfClosingTagIfEmpty: options.useSelfClosingTagIfEmpty
222 });
223 }
224 parseValue(arrayKey, item, element, options);
225 });
226}
227/**
228 * Parses an arbitrary JavaScript value into XML and adds it to the parent
229 * element.
230 *
231 * @private
232 */
233function parseValue(key, value, parentElement, options) {
234 // If a handler for a particular type is user-defined, use that handler
235 // instead of the defaults
236 var handler = getHandler(value, options);
237 if (!utils_1.isUndefined(handler)) {
238 value = handler(value);
239 }
240 if (utils_1.isObject(value) || utils_1.isMap(value)) {
241 parseObjectOrMap(value, parentElement, options);
242 return;
243 }
244 if (utils_1.isArray(value) || utils_1.isSet(value)) {
245 parseArrayOrSet(key, value, parentElement, options);
246 return;
247 }
248 parseString(utils_1.stringify(value), parentElement, options);
249}
250/**
251 * Converts the specified object to XML and adds the XML representation to the
252 * specified XmlDocument object using the specified options.
253 *
254 * This function does not add a root element. In addition, it does not add an
255 * XML declaration or DTD, and the associated options in {@link IOptions} are
256 * ignored. If desired, these must be added manually.
257 */
258function parseToExistingElement(element, object, options) {
259 var opts = new options_1.Options(options);
260 parseValue(element.name, object, element, opts);
261}
262exports.parseToExistingElement = parseToExistingElement;
263/**
264 * Returns a XML string representation of the specified object using the
265 * specified options.
266 *
267 * `root` is the name of the root XML element. When the object is converted
268 * to XML, it will be a child of this root element.
269 */
270function parse(root, object, options) {
271 var opts = new options_1.Options(options);
272 var document = new xmlcreate_1.XmlDocument({
273 validation: opts.validation
274 });
275 if (opts.declaration.include) {
276 document.decl(opts.declaration);
277 }
278 if (opts.dtd.include) {
279 document.dtd({
280 // Validated in options.ts
281 // @formatter:off
282 // eslint-disable-next-line max-len
283 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
284 name: opts.dtd.name,
285 // @formatter:on
286 pubId: opts.dtd.pubId,
287 sysId: opts.dtd.sysId
288 });
289 }
290 var rootElement = document.element({
291 name: root,
292 replaceInvalidCharsInName: opts.replaceInvalidChars,
293 useSelfClosingTagIfEmpty: opts.useSelfClosingTagIfEmpty
294 });
295 parseToExistingElement(rootElement, object, options);
296 return document.toString(opts.format);
297}
298exports.parse = parse;