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.
314 lines • 11.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Attribute = void 0;
const errors_1 = require("../errors");
const events_1 = require("../events");
const set_1 = require("../set");
const base_1 = require("./base");
const data_1 = require("./data");
const text_1 = require("./text");
/**
* A pattern for attributes.
*/
class Attribute extends base_1.Pattern {
/**
* @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 attribute.
*
* @param pat The pattern contained by this one.
*/
constructor(xmlPath, name, pat) {
super(xmlPath);
this.name = name;
this.pat = pat;
if (pat instanceof text_1.Text) {
this.kind = 1 /* Kind.TEXT */;
}
else if (pat instanceof data_1.Data) {
this.kind = 2 /* Kind.DATA */;
}
else {
this.kind = 0 /* Kind.DEFAULT */;
}
}
_prepare(definitions, namespaces) {
this.pat._prepare(definitions, namespaces);
this.name._recordNamespaces(namespaces, false);
}
hasAttrs() {
return true;
}
hasEmptyPattern() {
return false;
}
newWalker() {
switch (this.kind) {
case 1 /* Kind.TEXT */:
return new AttributeTextWalker(this, this.name, false, false, false);
case 2 /* Kind.DATA */:
const pat = this.pat;
return new AttributeDataWalker(this, this.name, pat.datatype, pat.params, pat.except, false, false, false);
default:
// tslint:disable-next-line:no-use-before-declare
return new AttributeWalker(this, this.name, this.pat.newWalker(), false, /* seenName */ false, false);
}
}
}
exports.Attribute = Attribute;
/**
* Walker for [[Attribute]].
*/
class AttributeWalker {
/**
* @param el The pattern for which this walker was created.
*/
constructor(el, name, subwalker, seenName, canEndAttribute, canEnd) {
this.el = el;
this.name = name;
this.subwalker = subwalker;
this.seenName = seenName;
this.canEndAttribute = canEndAttribute;
this.canEnd = canEnd;
}
clone() {
return new AttributeWalker(this.el, this.name, this.subwalker.clone(), this.seenName, this.canEndAttribute, this.canEnd);
}
possible() {
return new Set();
}
possibleAttributes() {
if (this.canEnd) {
return new Set();
}
if (!this.seenName) {
return new Set([new events_1.AttributeNameEvent(this.name)]);
}
// Convert text events to attributeValue events.
return (0, set_1.map)(this.subwalker.possible(), ev => {
if (ev.name !== "text") {
throw new Error(`unexpected event type: ${ev.name}`);
}
return new events_1.AttributeValueEvent(ev.value, ev.documentation);
});
}
fireEvent(name, params, nameResolver) {
// If canEnd is true, we've done everything we could. So we don't
// want to match again.
if (this.canEnd) {
return new base_1.InternalFireEventResult(false);
}
let value;
if (this.seenName) {
if (name !== "attributeValue") {
return new base_1.InternalFireEventResult(false);
}
value = params[0];
}
else if ((name === "attributeNameAndValue" || name === "attributeName") &&
this.name.match(params[0], params[1])) {
this.seenName = true;
if (name === "attributeName") {
return new base_1.InternalFireEventResult(true);
}
value = params[2];
}
else {
return new base_1.InternalFireEventResult(false);
}
this.canEnd = true;
this.canEndAttribute = true;
if (value !== "" &&
!this.subwalker.fireEvent("text", [value], nameResolver).matched) {
return new base_1.InternalFireEventResult(false, [new errors_1.AttributeValueError("invalid attribute value", this.name)]);
}
return base_1.InternalFireEventResult.fromEndResult(this.subwalker.end());
}
endAttributes() {
if (this.canEnd) {
return false;
}
// We set the canEnd flags true even though we did not end properly. This
// prevents producing errors about the same attribute multiple times,
// because end is called by element walkers when leaveStartTag is
// encountered, and again when the element closes.
this.canEnd = true;
this.canEndAttribute = true;
return [this.seenName ?
new errors_1.AttributeValueError("attribute value missing", this.name) :
new errors_1.AttributeNameError("attribute missing", this.name)];
}
end() {
return false;
}
}
/**
* This is a specialized walker for attributes that contain <text/> (which is
* the default when an attribute does not have a more specific content
* specified).
*/
class AttributeTextWalker {
/**
* @param el The pattern for which this walker was created.
*/
constructor(el, name, seenName, canEndAttribute, canEnd) {
this.el = el;
this.name = name;
this.seenName = seenName;
this.canEndAttribute = canEndAttribute;
this.canEnd = canEnd;
}
clone() {
return new AttributeTextWalker(this.el, this.name, this.seenName, this.canEndAttribute, this.canEnd);
}
possible() {
return new Set();
}
possibleAttributes() {
return this.canEnd ?
new Set() :
new Set([this.seenName ?
new events_1.AttributeValueEvent(/^[^]*$/) :
new events_1.AttributeNameEvent(this.name)]);
}
fireEvent(name, params, nameResolver) {
// If canEnd is true, we've done everything we could. So we don't
// want to match again.
if (this.canEnd) {
return new base_1.InternalFireEventResult(false);
}
if (this.seenName) {
if (name !== "attributeValue") {
return new base_1.InternalFireEventResult(false);
}
}
else if ((name === "attributeNameAndValue" || name === "attributeName") &&
this.name.match(params[0], params[1])) {
this.seenName = true;
if (name === "attributeName") {
return new base_1.InternalFireEventResult(true);
}
}
else {
return new base_1.InternalFireEventResult(false);
}
this.canEnd = true;
this.canEndAttribute = true;
return new base_1.InternalFireEventResult(true);
}
endAttributes() {
if (this.canEnd) {
return false;
}
// We set the canEnd flags true even though we did not end properly. This
// prevents producing errors about the same attribute multiple times,
// because end is called by element walkers when leaveStartTag is
// encountered, and again when the element closes.
this.canEnd = true;
this.canEndAttribute = true;
return [this.seenName ?
new errors_1.AttributeValueError("attribute value missing", this.name) :
new errors_1.AttributeNameError("attribute missing", this.name)];
}
end() {
return false;
}
}
/**
* This is a specialized walker for attributes that contain <data> (which is
* rather common).
*/
class AttributeDataWalker {
/**
* @param el The pattern for which this walker was created.
*/
constructor(el, name, datatype, params, except, seenName, canEndAttribute, canEnd) {
this.el = el;
this.name = name;
this.datatype = datatype;
this.params = params;
this.except = except;
this.seenName = seenName;
this.canEndAttribute = canEndAttribute;
this.canEnd = canEnd;
}
clone() {
return new AttributeDataWalker(this.el, this.name, this.datatype, this.params, this.except, this.seenName, this.canEndAttribute, this.canEnd);
}
possible() {
return new Set();
}
possibleAttributes() {
return this.canEnd ?
new Set() :
new Set([this.seenName ?
new events_1.AttributeValueEvent(this.datatype.regexp) :
new events_1.AttributeNameEvent(this.name)]);
}
fireEvent(name, params, nameResolver) {
// If canEnd is true, we've done everything we could. So we don't
// want to match again.
if (this.canEnd) {
return new base_1.InternalFireEventResult(false, undefined, undefined, this.datatype);
}
let value;
if (this.seenName) {
if (name !== "attributeValue") {
return new base_1.InternalFireEventResult(false, undefined, undefined, this.datatype);
}
value = params[0];
}
else if ((name === "attributeNameAndValue" || name === "attributeName") &&
this.name.match(params[0], params[1])) {
this.seenName = true;
if (name === "attributeName") {
return new base_1.InternalFireEventResult(true, undefined, undefined, this.datatype);
}
value = params[2];
}
else {
return new base_1.InternalFireEventResult(false, undefined, undefined, this.datatype);
}
this.canEnd = true;
this.canEndAttribute = true;
if (this.datatype.disallows(value, this.params, this.datatype.needsContext ?
{ resolver: nameResolver } : undefined)) {
return new base_1.InternalFireEventResult(false, [new errors_1.AttributeValueError("invalid attribute value", this.name)], undefined, this.datatype);
}
if (this.except !== undefined) {
const walker = this.except.newWalker();
const exceptRet = walker.fireEvent(name, params, nameResolver);
// False, so the except does match the text, and so this pattern does
// not match it.
if (exceptRet.matched) {
return new base_1.InternalFireEventResult(false, undefined, undefined, this.datatype);
}
// Otherwise, it is undefined, in which case it means the except does
// not match the text, and we are fine. Or it would be possible for the
// walker to have returned an error but there is nothing we can do with
// such errors here.
}
return new base_1.InternalFireEventResult(true, undefined, undefined, this.datatype);
}
endAttributes() {
if (this.canEnd) {
return false;
}
// We set the canEnd flags true even though we did not end properly. This
// prevents producing errors about the same attribute multiple times,
// because end is called by element walkers when leaveStartTag is
// encountered, and again when the element closes.
this.canEnd = true;
this.canEndAttribute = true;
return [this.seenName ?
new errors_1.AttributeValueError("attribute value missing", this.name) :
new errors_1.AttributeNameError("attribute missing", this.name)];
}
end() {
return false;
}
}
// LocalWords: RNG's MPL RNG attributeName attributeValue ev params
// LocalWords: neutralizeAttribute
//# sourceMappingURL=attribute.js.map