diff --git a/src/ol/xml.js b/src/ol/xml.js new file mode 100644 index 0000000000..5d1a954e25 --- /dev/null +++ b/src/ol/xml.js @@ -0,0 +1,202 @@ +goog.provide('ol.xml'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom.NodeType'); +goog.require('goog.object'); + + +/** + * @typedef {function(Node, Array.<*>)} + */ +ol.xml.Parser; + + +/** + * @param {Node} node Node. + * @param {boolean} normalizeWhitespace Normalize whitespace. + * @return {string} All text content. + */ +ol.xml.getAllTextContent = function(node, normalizeWhitespace) { + return ol.xml.getAllTextContent_(node, normalizeWhitespace, []).join(''); +}; + + +/** + * @param {Node} node Node. + * @param {boolean} normalizeWhitespace Normalize whitespace. + * @param {Array.} accumulator Accumulator. + * @private + * @return {Array.} Accumulator. + */ +ol.xml.getAllTextContent_ = function(node, normalizeWhitespace, accumulator) { + if (node.nodeType == goog.dom.NodeType.CDATA_SECTION || + node.nodeType == goog.dom.NodeType.TEXT) { + if (normalizeWhitespace) { + // FIXME understand why goog.dom.getTextContent_ uses String here + accumulator.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, '')); + } else { + accumulator.push(node.nodeValue); + } + } else { + var n; + for (n = node.firstChild; !goog.isNull(n); n = n.nextSibling) { + ol.xml.getAllTextContent_(n, normalizeWhitespace, accumulator); + } + } + return accumulator; +}; + + +/** + * @param {function(this: T, Node, Array.<*>): (Array.<*>|undefined)} + * valueReader Value reader. + * @param {T=} opt_obj Scope. + * @return {ol.xml.Parser} Parser. + * @template T + */ +ol.xml.makeArrayExtender = function(valueReader, opt_obj) { + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_obj, node, objectStack); + if (goog.isDef(value)) { + goog.asserts.assert(goog.isArray(value)); + var array = /** @type {Array.<*>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isArray(array)); + goog.array.extend(array, value); + } + }); +}; + + +/** + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. + * @param {T=} opt_obj Scope. + * @return {ol.xml.Parser} Parser. + * @template T + */ +ol.xml.makeArrayPusher = function(valueReader, opt_obj) { + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_obj, node, objectStack); + if (goog.isDef(value)) { + var array = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isArray(array)); + array.push(value); + } + }); +}; + + +/** + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. + * @param {T=} opt_obj Scope. + * @return {ol.xml.Parser} Parser. + * @template T + */ +ol.xml.makeReplacer = function(valueReader, opt_obj) { + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_obj, node, objectStack); + if (goog.isDef(value)) { + objectStack[objectStack.length - 1] = value; + } + }); +}; + + +/** + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. + * @param {string=} opt_property Property. + * @param {T=} opt_obj Scope. + * @return {ol.xml.Parser} Parser. + * @template T + */ +ol.xml.makeObjectPropertySetter = function(valueReader, opt_property, opt_obj) { + goog.asserts.assert(goog.isDef(valueReader)); + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_obj, node, objectStack); + if (goog.isDef(value)) { + var object = /** @type {Object} */ + (objectStack[objectStack.length - 1]); + var property = goog.isDef(opt_property) ? + opt_property : node.localName; + goog.asserts.assert(goog.isObject(object)); + goog.object.set(object, property, value); + } + }); +}; + + +/** + * @param {Array.} namespaceURIs Namespace URIs. + * @param {Object.} parsers Parsers. + * @return {Object.>} Parsers NS. + */ +ol.xml.makeParsersNS = function(namespaceURIs, parsers) { + /** @type {Object.>} */ + var parsersNS = {}; + var i, ii; + for (i = 0, ii = namespaceURIs.length; i < ii; ++i) { + parsersNS[namespaceURIs[i]] = parsers; + } + return parsersNS; +}; + + +/** + * @param {Object.>} parsersNS + * Parsers by namespace. + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @param {*=} opt_obj Scope. + */ +ol.xml.parse = function(parsersNS, node, objectStack, opt_obj) { + var n; + for (n = node.firstChild; !goog.isNull(n); n = n.nextSibling) { + if (n.nodeType == goog.dom.NodeType.ELEMENT) { + var parsers = parsersNS[n.namespaceURI]; + if (goog.isDef(parsers)) { + var parser = parsers[n.localName]; + if (goog.isDef(parser)) { + parser.call(opt_obj, n, objectStack); + } + } + } + } +}; + + +/** + * @param {T} object Object. + * @param {Object.>} parsersNS + * Parsers by namespace. + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @param {*=} opt_obj Scope. + * @return {T|undefined} Object. + * @template T + */ +ol.xml.pushAndParse = function(object, parsersNS, node, objectStack, opt_obj) { + objectStack.push(object); + ol.xml.parse(parsersNS, node, objectStack, opt_obj); + return objectStack.pop(); +};