From 59e44f6c84ad23dc9094573d28ee6a72ecebffef Mon Sep 17 00:00:00 2001 From: Jos van den Oever Date: Fri, 14 Aug 2015 11:00:51 +0200 Subject: [PATCH 1/2] Parse more information from the RNG grammar. The parser now knows which elements or attributes are allowed on elements. It also know which elements may contain text. This information is not yet output. --- webodf/tools/odfRng2Config.js | 268 +++++++++++++++++++++++++++------- 1 file changed, 213 insertions(+), 55 deletions(-) diff --git a/webodf/tools/odfRng2Config.js b/webodf/tools/odfRng2Config.js index a48bb1ce4..07f56dcae 100644 --- a/webodf/tools/odfRng2Config.js +++ b/webodf/tools/odfRng2Config.js @@ -22,28 +22,73 @@ * @source: https://github.com/kogmbh/WebODF/ */ -function aggregate(callback) { - return function (collection, individual) { - return collection.concat(callback(individual)); - }; -} +"use strict"; -function toArray(nodeList) { - "use strict"; - return Array.prototype.slice.call(nodeList); +var rngns = "http://relaxng.org/ns/structure/1.0"; + +/** + * Check if a node is an element in the rng namespace and the given localName. + * @param {!string} localName + * @param {!Node} node + * @return {!boolean} + */ +function isRng(localName, node) { + if (node.nodeType !== 1) { + return false; + } + if (node.namespaceURI !== rngns) { + return false; + } + if (node.localName !== localName) { + return false; + } + return true; } -function getName(node) { - return node && node.getAttribute("name"); +/** + * @param {!Node} node + * @return {?Element} + */ +function getFirstElementNode(node) { + node = node.firstChild; + while (node && node.nodeType !== 1) { + node = node.nextSibling; + } + return node; } +/** + * Return all explicit names for an or element. + * and return nothing. + * + * @param {!Node} node + * @return {!Array.} + */ function getNames(node) { - var name = getName(node); - return name ? [name] : toArray(node.getElementsByTagName("name")).map(function (node) { - return node.textContent; - }); + var names = []; + if (node.hasAttribute("name")) { + names.push(node.getAttribute("name")); + } else { + node = getFirstElementNode(node); + if (isRng("choice", node)) { + node = getFirstElementNode(node); + } + while (node) { + if (isRng("name", node)) { + names.push(node.textContent); + } + node = node.nextSibling; + } + } + return names; } +/** + * Increase length of string by adding spaces. + * @param {!string} str + * @param {!number} length + * @return {!string} + */ function pad(str, length) { while (str.length < length) { str += " "; @@ -52,58 +97,171 @@ function pad(str, length) { } /** - * Extract container node information out of the supplied RNG schema document. - * This only does extremely simplistic parsing. - * - * @constructor - * @param {!Document} document + * Get all the elements from an RNG grammar. + * @param {!Element} grammar + * @return {!Object.} */ -function ExtractContainerInfo(document) { - /** - * @param {!Node} node - * @return {!Array.} - */ - function findParentElements(node) { - var refs; - - while (node && /(define|element)/.test(node.localName) === false) { - node = node.parentNode; +function getDefines(grammar) { + var defines = {}, + c = grammar.firstChild; + while (c) { + if (c.nodeType === 1 && isRng("define", c)) { + defines[c.getAttribute("name")] = c; } + c = c.nextSibling; + } + return defines; +} - if (node) { - if (node.localName === "element") { - return [node]; +/** + * Information about an attribute or element. + * @constructor + */ +function Info() { + /**@type {!Object.}*/ + this.refs = {}; + /**@type {!boolean}*/ + this.text = false; + /**@type {!boolean}*/ + this.data = false; + /**@type {!boolean}*/ + this.value = false; + /**@type {!Array. to that of or . + * @param {!Info} info + * @param {!string} ref + * @param {!Object.} defines + * @return {undefined} + */ +function addDefine(info, ref, defines) { + var define = defines[ref], + c; + if (define) { + info.text = info.text || define.text; + info.data = info.data || define.data; + info.value = info.value || define.value; + define.childElements.forEach(function (ce) { + if (info.childElements.indexOf(ce) === -1) { + info.childElements.push(ce); } - refs = toArray(document.querySelectorAll("ref[name='" + getName(node) + "']")); - return refs.reduce(aggregate(findParentElements), []); - } - return []; + }); + define.attributes.forEach(function (a) { + if (info.attributes.indexOf(a) === -1) { + info.attributes.push(a); + } + }); } +} - this.getTextElements = function() { - return toArray(document.getElementsByTagName("text")).reduce(aggregate(findParentElements), []); - }; +/** + * Add information from elements to the set of or + * elements. + * @param {!Object.} infos + * @param {!Object.} defines + * @return {undefined} + */ +function resolveDefines(infos, defines) { + Object.keys(infos).forEach(function (name) { + var info = infos[name]; + Object.keys(info.refs).forEach(function (ref) { + addDefine(info, ref, defines); + }); + }); +} + +/** + * Recursively collect information from all elements in an RNG grammar. + * If a is encountered, the corresponding is traversed. + * This is done only once for each . + * + * @param {!Element} e + * @param {!Object. Date: Fri, 14 Aug 2015 12:53:58 +0200 Subject: [PATCH 2/2] Support Relax NG element. --- webodf/tools/odfRng2Config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webodf/tools/odfRng2Config.js b/webodf/tools/odfRng2Config.js index 07f56dcae..1b2f1d81e 100644 --- a/webodf/tools/odfRng2Config.js +++ b/webodf/tools/odfRng2Config.js @@ -226,6 +226,9 @@ function handleChildElements(e, defs, current, elements, attributes, defines) { handleChildElements(c, defs, info, elements, attributes, defines); } else if (isRng("text", c)) { current.text = true; + } else if (isRng("mixed", c)) { + current.text = true; + handleChildElements(c, defs, current, elements, attributes, defines); } else if (isRng("data", c)) { current.data = true; } else if (isRng("value", c)) {