salve-annos
Version:
A fork with support for documentation of Salve, a Javascript library which implements a validator able to validate an XML document on the basis of a subset of RelaxNG.
217 lines • 8.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.step10 = step10;
/**
* Simplification step 10.
* @author Louis-Dominique Dubeau
* @license MPL 2.0
* @copyright 2013, 2014 Mangalam Research Center for Buddhist Languages
*/
const parser_1 = require("../parser");
const schema_validation_1 = require("../schema-validation");
const util_1 = require("./util");
// We do not do the checks on ``data`` and ``value`` here. They are done
// later. The upshot is that fragments of the schema that may be removed in
// later steps are not checked here.
function checkNames(el) {
if (el.local === "except") {
// parent cannot be undefined at this point.
// tslint:disable-next-line:no-non-null-assertion
switch (el.parent.local) {
case "anyName":
if ((0, util_1.findDescendantsByLocalName)(el, "anyName").length !== 0) {
throw new schema_validation_1.SchemaValidationError("an except in anyName has an anyName descendant");
}
break;
case "nsName": {
const { anyName: anyNames, nsName: nsNames } = (0, util_1.findMultiDescendantsByLocalName)(el, ["anyName", "nsName"]);
if (anyNames.length !== 0) {
throw new schema_validation_1.SchemaValidationError("an except in nsName has an anyName descendant");
}
if (nsNames.length !== 0) {
throw new schema_validation_1.SchemaValidationError("an except in nsName has an nsName descendant");
}
break;
}
default:
}
}
for (const child of el.children) {
if (!(0, parser_1.isElement)(child)) {
continue;
}
checkNames(child);
}
}
// tslint:disable-next-line:max-func-body-length
function walk(check, el) {
const { local, children } = el;
switch (local) {
case "choice":
case "group":
case "interleave":
if (children.length === 1) {
const replaceWith = children[0];
if (el.parent !== undefined) {
el.parent.replaceChildWith(el, replaceWith);
}
else {
el.removeChildAt(0); // This removes replaceWith from el.
// By this stage in the process, this is the only attribute that need
// be carried over.
const xmlns = el.getAttribute("xmlns");
if (xmlns !== undefined) {
replaceWith.setXMLNS(xmlns);
}
}
return replaceWith;
}
else {
while (children.length > 2) {
el.prependChild(parser_1.Element.makeElement(local, [children[0], children[1]]));
}
}
break;
case "element":
if (children.length > 2) {
el.appendChild(parser_1.Element.makeElement("group", children.slice(1)));
}
if (check) {
checkNames(children[0]);
}
break;
case "attribute":
if (children.length === 1) {
el.appendChild(parser_1.Element.makeElement("text", []));
}
if (check) {
checkNames(children[0]);
for (const attrName of (0, util_1.findMultiNames)(el, ["name"]).name) {
switch (attrName.getAttribute("ns")) {
case "":
if (attrName.text === "xmlns") {
throw new schema_validation_1.SchemaValidationError("found attribute with name xmlns outside all namespaces");
}
break;
// tslint:disable-next-line:no-http-string
case "http://www.w3.org/2000/xmlns":
throw new schema_validation_1.SchemaValidationError("found attribute in namespace http://www.w3.org/2000/xmlns");
default:
}
}
}
break;
case "define":
case "oneOrMore":
case "list":
if (children.length > 1) {
const group = parser_1.Element.makeElement("group", []);
group.grabChildren(el);
el.appendChild(group);
}
break;
case "zeroOrMore": {
el.local = "choice";
const oneOrMore = parser_1.Element.makeElement("oneOrMore", []);
if (children.length > 1) {
const group = parser_1.Element.makeElement("group", []);
group.grabChildren(el);
oneOrMore.appendChild(group);
}
else {
oneOrMore.grabChildren(el);
}
el.appendChildren([oneOrMore, parser_1.Element.makeElement("empty", [])]);
break;
}
case "optional":
el.local = "choice";
if (children.length > 1) {
const group = parser_1.Element.makeElement("group", []);
group.grabChildren(el);
el.appendChild(group);
}
el.appendChild(parser_1.Element.makeElement("empty", []));
break;
case "mixed":
el.local = "interleave";
if (children.length > 1) {
const group = parser_1.Element.makeElement("group", []);
group.grabChildren(el);
el.appendChild(group);
}
el.appendChild(parser_1.Element.makeElement("text", []));
break;
case "except":
if (children.length > 1) {
const choice = parser_1.Element.makeElement("choice", []);
choice.grabChildren(el);
el.appendChild(choice);
}
break;
default:
}
// tslint:disable-next-line:prefer-for-of
for (let ix = 0; ix < children.length; ++ix) {
const child = children[ix];
if (!(0, parser_1.isElement)(child)) {
continue;
}
let replaced = walk(check, child);
while (replaced !== null) {
replaced = walk(check, replaced);
}
}
return null;
}
/**
* Implements steps 10 to 13 of the XSL pipeline. Namely:
*
* - ``define``, ``oneOrMore``, ``zeroOrMore``, ``optional``, ``list`` and
* ``mixed`` elements with more than one child have their children wrapped in
* a ``group`` element.
*
* - ``element`` elements with more than two children have their children
* wrapped in a ``group`` element. (This means more than one child in addition
* to the name class which is the 1sts element of ``element`` at this point.)
*
* - ``except`` elements with more than one child have their children wrapped in
* a ``group`` element.
*
* - ``attribute`` elements with only one child (only a ``name`` element) get a
* ``<text/>`` child.
*
* - ``choice``, ``group`` and ``interleave`` elements with only one child are
* replaced by their single child.
*
* - ``choice``, ``group`` and ``interleave`` elements with more than 2 children
* have their first 2 children wrapped in an element of the same name, and
* this is repeated until the top level element contains only two
* children. (This transformation applies to elements that were part of the
* input and those elements created by the transformations above.)
*
* - ``mixed`` elements are converted to ``interleave`` elements containing the
* single child of ``mixed``, and ``<text/>``.
*
* - ``optional`` elements are converted to ``choice`` elements containing the
* single child of ``optional``, and ``<empty/>``.
*
* - ``zeroOrMore`` elements are converted to ``choice`` elements. The single
* child of the original ``zeroOrMore`` element is wrapped in a ``oneOrMore``
* element, and ``<empty/>`` is added to the ``choice`` element.
*
* @param el The tree to process. It is modified in-place.
*
* @param check Whether to perform constraint checks.
*
* @returns The new root of the tree.
*/
function step10(el, check) {
let replaced = walk(check, el);
while (replaced !== null) {
el = replaced;
replaced = walk(check, el);
}
return el;
}
//# sourceMappingURL=step10.js.map