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.
170 lines • 6.64 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Element = void 0;
/**
* Pattern and walker for RNG's ``element`` elements.
* @author Louis-Dominique Dubeau
* @license MPL 2.0
* @copyright Mangalam Research Center for Buddhist Languages
*/
const errors_1 = require("../errors");
const events_1 = require("../events");
const base_1 = require("./base");
const not_allowed_1 = require("./not_allowed");
/**
* A pattern for elements.
*/
class Element extends base_1.BasePattern {
/**
* @param xmlPath This is a string which uniquely identifies the
* element from the simplified RNG tree. Used in debugging.
*
* @param name The qualified name of the element.
*
* @param pat The pattern contained by this one.
*/
constructor(xmlPath, name, pat) {
super(xmlPath);
this.name = name;
this.pat = pat;
this.notAllowed = pat instanceof not_allowed_1.NotAllowed;
}
newWalker(boundName) {
// tslint:disable-next-line:no-use-before-declare
return new ElementWalker(this, this.pat.hasAttrs(), this.pat.newWalker(), false, new events_1.EndTagEvent(boundName), boundName, true, false);
}
hasAttrs() {
return false;
}
hasEmptyPattern() {
// The question is whether an element allows empty content **in the context
// in which it appears**, not empty content inside it. So the answer is
// always negative.
return false;
}
_prepare(definitions, namespaces) {
this.name._recordNamespaces(namespaces, true);
this.pat._prepare(definitions, namespaces);
}
}
exports.Element = Element;
/**
*
* Walker for [[Element]].
*/
class ElementWalker {
constructor(el, hasAttrs, walker, endedStartTag, endTagEvent, boundName, canEndAttribute, canEnd) {
this.el = el;
this.hasAttrs = hasAttrs;
this.walker = walker;
this.endedStartTag = endedStartTag;
this.endTagEvent = endTagEvent;
this.boundName = boundName;
this.canEndAttribute = canEndAttribute;
this.canEnd = canEnd;
}
clone() {
return new ElementWalker(this.el, this.hasAttrs, this.walker.clone(), this.endedStartTag, this.endTagEvent, this.boundName, this.canEndAttribute, this.canEnd);
}
possible() {
// Contrarily to all other implementations, which must only return
// non-attribute events. This implementation actually returns all types of
// possible events. Representing this distinction through TS type
// declarations would be cumbersome. The exception works because of the way
// Relax NG constrains the structure of a simplified schema. The only
// possible caller for this method is ``GrammarWalker``, which also aims to
// return all possible events.
if (!this.endedStartTag) {
const walker = this.walker;
const ret = walker.possibleAttributes();
if (walker.canEndAttribute) {
ret.add(ElementWalker._leaveStartTagEvent);
}
return ret;
}
else if (!this.canEnd) {
const walker = this.walker;
const posses = walker.possible();
if (walker.canEnd) {
posses.add(this.endTagEvent);
}
return posses;
}
return new Set();
}
possibleAttributes() {
throw new Error("calling possibleAttributes on ElementWalker is invalid");
}
/**
* Initialize this walker with initial attributes. This is provided to support
* ``startTagAndAttributes``.
*
* @param params The **entire** list of parameters passed with the
* ``startTagAndAttributes`` event.
*
* @param nameResolver The name resolver to use to resolve names.
*
* @returns The result of firing all the events for the attributes.
*/
initWithAttributes(params, nameResolver) {
const { walker } = this;
// We need to handle all attributes and leave the start tag.
for (let ix = 2; ix < params.length; ix += 3) {
const attrRet = walker.fireEvent("attributeNameAndValue", [params[ix], params[ix + 1],
params[ix + 2]], nameResolver);
if (!attrRet.matched) {
return attrRet;
}
}
// Make leaveStartTag effective.
this.endedStartTag = true;
return this.hasAttrs ?
base_1.InternalFireEventResult.fromEndResult(walker.endAttributes()) :
new base_1.InternalFireEventResult(true);
}
fireEvent(name, params, nameResolver) {
// This is not a useful optimization. canEnd becomes true once we see
// the end tag, which means that this walker will be popped of
// GrammarWalker's stack and won't be called again.
//
// if (this.canEnd) {
// return undefined;
// }
const walker = this.walker;
if (this.endedStartTag) {
if (name === "endTag") {
// We cannot get here if canEnd is already true. So this will
// necessarily leave it false or set it to true but it won't set a true
// canEnd back to false.
this.canEnd = this.boundName.match(params[0], params[1]);
return base_1.InternalFireEventResult.fromEndResult(walker.end());
}
return walker.fireEvent(name, params, nameResolver);
}
if (name === "leaveStartTag") {
this.endedStartTag = true;
return this.hasAttrs ?
base_1.InternalFireEventResult.fromEndResult(walker.endAttributes()) :
new base_1.InternalFireEventResult(true);
}
return walker.fireEvent(name, params, nameResolver);
}
end() {
if (this.canEnd) {
return false;
}
const err = new errors_1.ElementNameError(this.endedStartTag ?
"tag not closed" :
"start tag not terminated", this.boundName);
const walkerRet = this.walker.end();
return walkerRet ? walkerRet.concat(err) : [err];
}
endAttributes() {
throw new Error("calling endAttributes on ElementWalker is illegal");
}
}
ElementWalker._leaveStartTagEvent = new events_1.LeaveStartTagEvent();
// LocalWords: RNG's MPL RNG ElementWalker leaveStartTag valueEvs
// LocalWords: enterStartTag attributeValue endTag errored subwalker
// LocalWords: neutralizeAttribute boundName fireEvent suppressAttributes
//# sourceMappingURL=element.js.map