typesxml
Version:
Open source XML library written in TypeScript
229 lines • 7.48 kB
JavaScript
/*******************************************************************************
* Copyright (c) 2023-2026 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
*******************************************************************************/
import { CData } from "./CData.js";
import { ProcessingInstruction } from "./ProcessingInstruction.js";
import { TextNode } from "./TextNode.js";
import { XMLComment } from "./XMLComment.js";
import { XMLDeclaration } from "./XMLDeclaration.js";
import { XMLDocument } from "./XMLDocument.js";
import { XMLDocumentType } from "./XMLDocumentType.js";
import { XMLElement } from "./XMLElement.js";
import { XMLUtils } from "./XMLUtils.js";
import { DTDParser } from "./dtd/DTDParser.js";
import { DTDGrammar } from "./dtd/DTDGrammar.js";
export class DOMBuilder {
inCdData = false;
currentCData = new CData('');
document;
stack = [];
catalog;
dtdParser;
grammar;
initialize() {
this.document = new XMLDocument();
this.stack = new Array();
this.inCdData = false;
}
setGrammar(grammar) {
this.grammar = grammar;
if (grammar instanceof DTDGrammar) {
if (!this.dtdParser) {
this.dtdParser = new DTDParser(grammar);
}
else {
this.dtdParser.setGrammar(grammar);
}
}
}
setCatalog(catalog) {
this.catalog = catalog;
}
getDocument() {
return this.document;
}
startDocument() {
// do nothing
}
endDocument() {
// do nothing
}
xmlDeclaration(version, encoding, standalone) {
let xmlDclaration = new XMLDeclaration(version, encoding, standalone);
this.document?.setXmlDeclaration(xmlDclaration);
}
startElement(name, atts) {
let element = new XMLElement(name);
atts.forEach((att) => {
element.setAttribute(att);
});
if (this.stack.length > 0) {
this.stack[this.stack.length - 1].addElement(element);
}
else {
this.document?.setRoot(element);
}
this.stack.push(element);
}
endElement(name) {
this.stack.pop();
}
getCurrentText() {
if (this.stack.length > 0) {
return this.stack[this.stack.length - 1].pureText();
}
return '';
}
internalSubset(declaration) {
let docType = this.document?.getDocumentType();
if (docType) {
docType.setInternalSubset(declaration);
}
}
characters(ch) {
if (this.inCdData) {
this.currentCData.setValue(this.currentCData.getValue() + ch);
return;
}
let textNode = new TextNode(ch);
if (this.stack.length > 0) {
this.stack[this.stack.length - 1].addTextNode(textNode);
}
else {
this.document?.addTextNode(textNode);
}
}
ignorableWhitespace(ch) {
let textNode = new TextNode(ch);
if (this.stack.length > 0) {
this.stack[this.stack.length - 1].addTextNode(textNode);
}
else {
this.document?.addTextNode(textNode);
}
}
comment(ch) {
let comment = new XMLComment(ch);
if (this.stack.length > 0) {
this.stack[this.stack.length - 1].addComment(comment);
}
else {
this.document?.addComment(comment);
}
}
processingInstruction(target, data) {
let pi = new ProcessingInstruction(target, data);
if (this.stack.length > 0) {
this.stack[this.stack.length - 1].addProcessingInstruction(pi);
}
else {
this.document?.addProcessingInstruction(pi);
}
}
parseXmlModel(text) {
let map = new Map();
let pairs = [];
let separator = '';
while (text.indexOf('=') != -1) {
let i = 0;
for (; i < text.length; i++) {
let char = text[i];
if (XMLUtils.isXmlSpace(char) || '=' === char) {
break;
}
}
for (; i < text.length; i++) {
let char = text[i];
if (separator === '' && ('\'' === char || '"' === char)) {
separator = char;
continue;
}
if (char === separator) {
break;
}
}
// end of value
let pair = text.substring(0, i + 1).trim();
pairs.push(pair);
text = text.substring(pair.length).trim();
separator = '';
}
pairs.forEach((pair) => {
let index = pair.indexOf('=');
if (index === -1) {
throw new Error('Malformed attributes list');
}
let name = pair.substring(0, index).trim();
let value = pair.substring(index + 2, pair.length - 1);
map.set(name, value);
});
return map;
}
startCDATA() {
this.currentCData = new CData('');
this.inCdData = true;
}
endCDATA() {
if (this.stack.length > 0) {
this.stack[this.stack.length - 1].addCData(this.currentCData);
}
else {
throw new Error("CData section outside of root element");
}
this.inCdData = false;
}
startDTD(name, publicId, systemId) {
let docType = new XMLDocumentType(name, publicId, systemId);
this.document?.setDocumentType(docType);
if (this.catalog) {
const url = this.catalog.resolveEntity(publicId, systemId);
if (url) {
if (!this.dtdParser) {
this.dtdParser = new DTDParser();
}
const dtdGrammar = this.dtdParser.parseDTD(url);
this.setGrammar(dtdGrammar);
}
}
}
endDTD() {
// do nothing
}
getGrammar() {
return this.grammar;
}
skippedEntity(name) {
const replacement = this.grammar?.resolveEntity(name);
if (replacement && replacement.length > 0) {
this.characters(replacement);
return;
}
if (this.grammar instanceof DTDGrammar) {
const entityDecl = this.grammar.getEntity(name);
if (entityDecl && entityDecl.isExternal() && this.dtdParser) {
try {
const externalText = this.dtdParser.loadExternalEntity(entityDecl.getPublicId(), entityDecl.getSystemId(), true);
if (externalText.length > 0) {
entityDecl.setValue(externalText);
this.characters(externalText);
return;
}
}
catch (error) {
throw new Error(`Could not resolve external entity "${name}": ${error.message}`);
}
}
}
// Preserve the reference if no replacement text is available
this.characters('&' + name + ';');
}
}
//# sourceMappingURL=DOMBuilder.js.map