/*
* @copyright
* Copyright © Microsoft Open Technologies, Inc.
*
* All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http: *www.apache.org/licenses/LICENSE-2.0
*
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
* ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A
* PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT.
*
* See the Apache License, Version 2.0 for the specific language
* governing permissions and limitations under the License.
*/
'use strict';
var _ = require('underscore');
var select = require('xpath.js');
var XMLSerializer = require('xmldom').XMLSerializer;
var constants = require('./constants');
/**
* @namespace XmlUtil
* @private
*/
var XPATH_PATH_TEMPLATE = '*[local-name() = \'LOCAL_NAME\' and namespace-uri() = \'NAMESPACE\']';
/**
* The xpath implementation being used does not have a way of matching expanded namespace.
* This method takes an xpath query and expands all of the namespaces involved. It then
* re-writes the query in to a longer form that directory matches the correct namespaces.
* @private
* @static
* @memberOf XmlUtil
* @param {string} xpath The expath query string to expand.
* @returns {string} An expanded xpath query.
*/
function expandQNames(xpath) {
var namespaces = constants.XmlNamespaces;
var pathParts = xpath.split('/');
for (var i=0; i < pathParts.length; i++) {
if (pathParts[i].indexOf(':') !== -1) {
var QNameParts = pathParts[i].split(':');
Iif (QNameParts.length !== 2) {
throw new Error('Unable to parse XPath string : ' + xpath + ' : with QName : ' + pathParts[i]);
}
var expandedPath = XPATH_PATH_TEMPLATE.replace('LOCAL_NAME', QNameParts[1]);
expandedPath = expandedPath.replace('NAMESPACE', namespaces[QNameParts[0]]);
pathParts[i] = expandedPath;
}
}
return pathParts.join('/');
}
var exports = {
/**
* Performs an xpath select that does appropriate namespace matching since the imported
* xpath module does not properly handle namespaces.
* @static
* @memberOf XmlUtil
* @param {object} dom A dom object created by the xmldom module
* @param {string} xpath An xpath expression
* @return {array} An array of matching dom nodes.
*/
xpathSelect : function (dom, xpath) {
return select(dom, expandQNames(xpath));
},
/**
* Given a dom node serializes all immediate children that are xml elements.
* @static
* @memberOf XmlUtil
* @param {object} node An xml dom node.
* @return {string} Serialized xml.
*/
serializeNodeChildren : function(node) {
var doc = '';
var sibling = node.firstChild;
var serializer = new XMLSerializer();
while (sibling) {
if (this.isElementNode(sibling)) {
doc += serializer.serializeToString(sibling);
}
sibling = sibling.nextSibling;
}
return doc !== '' ? doc : null;
},
/**
* Detects whether the passed in dom node represents an xml element.
* @static
* @memberOf XmlUtil
* @param {object} node An xml dom node.
* @return {Boolean} true if the node represents an element.
*/
isElementNode : function(node) {
return _.has(node, 'tagName');
},
/**
* Given an xmldom node this function returns any text data contained within.
* @static
* @memberOf XmlUtil
* @param {object} node An xmldom node from which the data should be extracted.
* @return {string} Any data found within the element or null if none is found.
*/
findElementText : function(node) {
var sibling = node.firstChild;
while (sibling && !sibling.data) {
sibling = sibling.nextSibling;
}
return sibling.data ? sibling.data : null;
}
};
module.exports = exports; |