typesxml
Version:
Open source XML library written in TypeScript
775 lines • 30.1 kB
JavaScript
"use strict";
/*******************************************************************************
* Copyright (c) 2023 - 2024 Maxprograms.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse License 1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-v10.html
*
* Contributors:
* Maxprograms - initial API and implementation
*******************************************************************************/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DTDParser = void 0;
const fs_1 = require("fs");
const path = __importStar(require("node:path"));
const XMLUtils_1 = require("../XMLUtils");
const Grammar_1 = require("../grammar/Grammar");
const AttListDecl_1 = require("./AttListDecl");
const ElementDecl_1 = require("./ElementDecl");
const EntityDecl_1 = require("./EntityDecl");
const NotationDecl_1 = require("./NotationDecl");
class DTDParser {
grammar;
catalog;
pointer = 0;
source;
currentFile;
constructor(grammar) {
this.currentFile = '';
if (grammar) {
this.grammar = grammar;
}
else {
this.grammar = new Grammar_1.Grammar();
}
}
setCatalog(catalog) {
this.catalog = catalog;
}
parseDTD(file) {
this.parseFile(file);
this.grammar.processModels();
return this.grammar;
}
parseFile(file) {
this.source = '';
let stats = (0, fs_1.statSync)(file, { bigint: false, throwIfNoEntry: true });
this.currentFile = file;
let blockSize = stats.blksize;
let fileHandle = (0, fs_1.openSync)(file, 'r');
let buffer = Buffer.alloc(blockSize);
let bytesRead = (0, fs_1.readSync)(fileHandle, buffer, 0, blockSize, 0);
while (bytesRead > 0) {
this.source += buffer.toString('utf8', 0, bytesRead);
bytesRead = (0, fs_1.readSync)(fileHandle, buffer, 0, blockSize, this.source.length);
}
(0, fs_1.closeSync)(fileHandle);
return this.parse();
}
parseString(source) {
this.source = source;
this.parse();
this.grammar.processModels();
return this.grammar;
}
parse() {
this.pointer = 0;
while (this.pointer < this.source.length) {
if (this.lookingAt('<!ELEMENT')) {
let index = this.source.indexOf('>', this.pointer);
if (index === -1) {
throw new Error('Malformed element declaration');
}
let elementText = this.source.substring(this.pointer, index + '>'.length);
let length = elementText.length;
let elementDecl = this.parseElementDeclaration(elementText);
this.grammar.addElement(elementDecl);
this.pointer += length;
continue;
}
if (this.lookingAt('<!ATTLIST')) {
let index = this.source.indexOf('>', this.pointer);
if (index === -1) {
throw new Error('Malformed attribute declaration');
}
let attListText = this.source.substring(this.pointer, index + '>'.length);
let length = attListText.length;
let attList = this.parseAttributesListDeclaration(attListText);
this.grammar.addAttributes(attList.getName(), attList.getAttributes());
this.pointer += length;
continue;
}
if (this.lookingAt('<!ENTITY')) {
let index = this.source.indexOf('>', this.pointer);
if (index === -1) {
throw new Error('Malformed entity declaration');
}
let entityDeclText = this.source.substring(this.pointer, index + '>'.length);
let entityDecl = this.parseEntityDeclaration(entityDeclText);
this.grammar.addEntity(entityDecl);
this.pointer += entityDeclText.length;
continue;
}
if (this.lookingAt('<!NOTATION')) {
let index = this.source.indexOf('>', this.pointer);
if (index === -1) {
throw new Error('Malformed notation declaration');
}
let notationDeclText = this.source.substring(this.pointer, index + '>'.length);
if (XMLUtils_1.XMLUtils.hasParameterEntity(notationDeclText)) {
notationDeclText = this.resolveEntities(notationDeclText);
}
let notation = this.parseNotationDeclaration(notationDeclText);
this.grammar.addNotation(notation);
this.pointer += notationDeclText.length;
continue;
}
if (this.lookingAt('<![')) {
this.parseConditionalSection();
continue;
}
if (this.lookingAt(']]>')) {
this.endConditionalSection();
continue;
}
if (this.lookingAt('<?')) {
let index = this.source.indexOf('?>', this.pointer);
if (index === -1) {
throw new Error('Malformed processing instruction');
}
// skip processing instructions
this.pointer = index + '?>'.length;
continue;
}
if (this.lookingAt('<!--')) {
let index = this.source.indexOf('-->', this.pointer);
if (index === -1) {
throw new Error('Malformed comment');
}
// skip comments
this.pointer = index + '-->'.length;
continue;
}
if (this.lookingAt('%')) {
let index = this.source.indexOf(';', this.pointer);
if (index == -1) {
throw new Error('Malformed entity reference');
}
let entityName = this.source.substring(this.pointer + '%'.length, index);
let entity = this.grammar.getEntity(entityName);
if (entity === undefined) {
throw new Error('Unknown entity: ' + entityName);
}
let value = entity.getValue();
if (value !== '') {
let start = this.source.substring(0, this.pointer);
let end = this.source.substring(index + ';'.length);
this.source = start + value + end;
this.pointer += value.length;
}
else if (entity.getSystemId() !== '' || entity.getPublicId() !== '') {
let location = this.resolveEntity(entity.getPublicId(), entity.getSystemId());
let parser = new DTDParser(this.grammar);
parser.setCatalog(this.catalog);
let externalGrammar = parser.parseFile(location);
this.grammar.merge(externalGrammar);
this.pointer = index + ';'.length;
}
else {
// empty entity, ignore
this.pointer = index + ';'.length;
}
continue;
}
let char = this.source.charAt(this.pointer);
if (XMLUtils_1.XMLUtils.isXmlSpace(char)) {
this.pointer++;
continue;
}
throw new Error('Error parsing ' + this.currentFile + ' at ' + this.source.substring(this.pointer - 10, this.pointer) + ' @ ' + this.source.substring(this.pointer, this.pointer + 30));
}
return this.grammar;
}
endConditionalSection() {
// jump over ]]>
this.pointer += ']]>'.length;
}
parseConditionalSection() {
this.pointer += '<!['.length;
// skip spaces before section keyword
for (; this.pointer < this.source.length; this.pointer++) {
let char = this.source.charAt(this.pointer);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
// read section keyword
let keyword = '';
for (; this.pointer < this.source.length; this.pointer++) {
let char = this.source.charAt(this.pointer);
if (XMLUtils_1.XMLUtils.isXmlSpace(char) || char === '[') {
break;
}
keyword += char;
}
if (XMLUtils_1.XMLUtils.hasParameterEntity(keyword)) {
keyword = this.resolveEntities(keyword);
}
if ('INCLUDE' === keyword) {
// jump to the start of the content
for (; this.pointer < this.source.length; this.pointer++) {
let char = this.source.charAt(this.pointer);
if (char === '[') {
break;
}
}
this.pointer++;
}
else if ('IGNORE' === keyword) {
this.skipIgnoreSection();
}
else {
throw new Error('Malformed conditional section');
}
}
skipIgnoreSection() {
let stack = 1;
while (this.pointer < this.source.length) {
if (this.lookingAt('<![')) {
stack++;
this.pointer += '<!['.length;
}
else if (this.lookingAt(']]>')) {
stack--;
this.pointer += ']]>'.length;
if (stack === 0) {
return;
}
}
else {
this.pointer++;
}
}
}
resolveEntities(fragment) {
while (XMLUtils_1.XMLUtils.hasParameterEntity(fragment)) {
let start = fragment.indexOf('%');
let end = fragment.indexOf(';');
let entityName = fragment.substring(start + '%'.length, end);
let entity = this.grammar.getEntity(entityName);
if (entity === undefined) {
throw new Error('Unknown entity: ' + entityName);
}
fragment = fragment.replace('%' + entityName + ';', entity.getValue());
}
return fragment;
}
parseEntityDeclaration(declaration) {
let name = '';
let i = '<!ENTITY'.length;
let char = declaration.charAt(i);
// skip spaces before % or entity name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let parameterEntity = false;
if (char === '%') {
parameterEntity = true;
// skip spaces before name
i++;
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
}
// get entity name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
name += char;
}
if (XMLUtils_1.XMLUtils.hasParameterEntity(name)) {
name = this.resolveEntities(name);
}
// skip spaces before entity value or external id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
if (parameterEntity) {
// can have value or external id
if (XMLUtils_1.XMLUtils.lookingAt('PUBLIC', declaration, i)) {
i += 'PUBLIC'.length;
// skip spaces before public id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let separator = declaration.charAt(i);
i++; // skip opening "
// get public id
let publicId = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
publicId += char;
}
i++; // skip closing "
if (XMLUtils_1.XMLUtils.hasParameterEntity(publicId)) {
publicId = this.resolveEntities(publicId);
}
// skip spaces before system id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
separator = declaration.charAt(i);
i++; // skip opening "
// get system id
let systemId = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
systemId += char;
}
if (XMLUtils_1.XMLUtils.hasParameterEntity(systemId)) {
systemId = this.resolveEntities(systemId);
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, '', systemId, publicId, '');
}
else if (XMLUtils_1.XMLUtils.lookingAt('SYSTEM', declaration, i)) {
// skip spaces before system id
i += 'SYSTEM'.length;
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let separator = declaration.charAt(i);
i++; // skip opening "
// get system id
let systemId = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
systemId += char;
}
if (XMLUtils_1.XMLUtils.hasParameterEntity(systemId)) {
systemId = this.resolveEntities(systemId);
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, '', systemId, '', '');
}
else {
// get entity value
let separator = declaration.charAt(i);
i++; // skip opening "
let value = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
value += char;
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, value, '', '', '');
}
}
else {
// Not a parameterEntity. Similar, but may declare NDATA
if (XMLUtils_1.XMLUtils.lookingAt('PUBLIC', declaration, i)) {
i += 'PUBLIC'.length;
// skip spaces before public id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let separator = declaration.charAt(i);
i++; // skip "
// get public id
let publicId = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
publicId += char;
}
i++; // skip closing "
if (XMLUtils_1.XMLUtils.hasParameterEntity(publicId)) {
publicId = this.resolveEntities(publicId);
}
// skip spaces before system id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
separator = declaration.charAt(i);
i++; // skip "
// get system id
let systemId = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
systemId += char;
}
i++; // skip closing "
if (XMLUtils_1.XMLUtils.hasParameterEntity(systemId)) {
systemId = this.resolveEntities(systemId);
}
// skip spaces before NDATA
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
if (XMLUtils_1.XMLUtils.lookingAt('NDATA', declaration, i)) {
i += 'NDATA'.length;
// skip spaces before ndata name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
// get ndata name
let ndata = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
ndata += char;
}
if (XMLUtils_1.XMLUtils.hasParameterEntity(ndata)) {
ndata = this.resolveEntities(ndata);
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, '', systemId, publicId, ndata);
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, '', systemId, publicId, '');
}
else if (XMLUtils_1.XMLUtils.lookingAt('SYSTEM', declaration, i)) {
i += 'SYSTEM'.length;
// skip spaces before system id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let separator = declaration.charAt(i);
i++; // skip "
// get system id
let systemId = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
systemId += char;
}
if (XMLUtils_1.XMLUtils.hasParameterEntity(systemId)) {
systemId = this.resolveEntities(systemId);
}
// skip spaces before NDATA
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
if (XMLUtils_1.XMLUtils.lookingAt('NDATA', declaration, i)) {
i += 'NDATA'.length;
// skip spaces before ndata name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
// get ndata name
let ndata = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
ndata += char;
}
if (XMLUtils_1.XMLUtils.hasParameterEntity(ndata)) {
ndata = this.resolveEntities(ndata);
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, '', systemId, '', ndata);
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, '', systemId, '', '');
}
else {
// get entity value
let separator = declaration.charAt(i);
i++; // skip "
let value = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
value += char;
}
return new EntityDecl_1.EntityDecl(name, parameterEntity, value, '', '', '');
}
}
}
parseNotationDeclaration(declaration) {
let name = '';
let i = '<!NOTATION'.length;
let char = declaration.charAt(i);
// skip spaces before notation name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
// get notation name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
name += char;
}
// skip spaces before external id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let publicId = '';
let systemId = '';
if (XMLUtils_1.XMLUtils.lookingAt('PUBLIC', declaration, i)) {
i += 'PUBLIC'.length;
// skip spaces before public id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let separator = declaration.charAt(i);
i++; // skip opening "
// get public id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
publicId += char;
}
i++; // skip closing "
if (XMLUtils_1.XMLUtils.hasParameterEntity(publicId)) {
publicId = this.resolveEntities(publicId);
}
// skip spaces before system id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
separator = declaration.charAt(i);
i++; // skip opening "
// get system id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
systemId += char;
}
}
else if (XMLUtils_1.XMLUtils.lookingAt('SYSTEM', declaration, i)) {
i += 'SYSTEM'.length;
// skip spaces before system id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let separator = declaration.charAt(i);
i++; // skip opening "
// get system id
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === separator) {
break;
}
systemId += char;
}
}
else {
throw new Error('Malformed notation declaration');
}
return new NotationDecl_1.NotationDecl(name, publicId, systemId);
}
parseAttributesListDeclaration(declaration) {
let i = '<!ATTLIST'.length;
let char = declaration.charAt(i);
// skip spaces before list name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
// get list name
let name = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
name += char;
}
// skip spaces before attributes declaration
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
let attributesText = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === '>') {
break;
}
attributesText += char;
}
let list = new AttListDecl_1.AttListDecl(name, attributesText);
return list;
}
parseElementDeclaration(declaration) {
let name = '';
let i = '<!ELEMENT'.length;
let char = declaration.charAt(i);
// skip spaces before element name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
// get element name
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
name += char;
}
// skip spaces before content spec
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (!XMLUtils_1.XMLUtils.isXmlSpace(char)) {
break;
}
}
// get content spec
let contentSpec = '';
for (; i < declaration.length; i++) {
char = declaration.charAt(i);
if (char === '>') {
break;
}
contentSpec += char;
}
return new ElementDecl_1.ElementDecl(name, contentSpec);
}
lookingAt(text) {
let length = text.length;
if (this.pointer + length > this.source.length) {
return false;
}
for (let i = 0; i < length; i++) {
if (this.source[this.pointer + i] !== text[i]) {
return false;
}
}
return true;
}
resolveEntity(publicId, systemId) {
let location = this.catalog.resolveEntity(publicId, systemId);
if (!location && systemId !== '' && !systemId.startsWith('http')) {
location = this.makeAbsolute(systemId);
}
if (location) {
return location;
}
if (systemId.startsWith('http')) {
return systemId;
}
throw new Error('Entity not found: "' + publicId + '" "' + systemId + '"');
}
makeAbsolute(uri) {
let currentPath = path.dirname(this.currentFile);
return currentPath + path.sep + uri;
}
getGrammar() {
return this.grammar;
}
}
exports.DTDParser = DTDParser;
//# sourceMappingURL=DTDParser.js.map