@openxmldev/linq-to-xml
Version:
LINQ to XML for TypeScript
156 lines • 5.92 kB
JavaScript
/**
* @author Thomas Barnekow
* @license MIT
*/
import { ArgumentException, NamespaceResolver, XElement, XNamespace, XProcessingInstruction, XText, } from './internal.js';
/**
* Transforms Linq-to-XML into DOM documents or elements, i.e.:
* - `XDocument` into `XMLDocument` and
* - `XElement` into `Element`.
*
* @internal
*/
export class DomFactory {
constructor() {
this.xmlDocument = document.implementation.createDocument(null, null);
this.resolver = new NamespaceResolver();
}
/**
* Creates a DOM `XMLDocument` from the given `XDocument`.
*
* @param xdocument The `XDocument`.
* @returns A new `XMLDocument`.
*/
static createDocument(xdocument) {
return new DomFactory().createDocument(xdocument);
}
/**
* Creates a DOM `Element` from the given `XElement`.
*
* @param xelement The `XElement`.
* @returns A new `Element`.
*/
static createElement(xelement) {
const domFactory = new DomFactory();
domFactory.pushAncestors(xelement);
return domFactory.createElement(xelement);
}
createDocument(xdocument) {
if (xdocument.declaration !== null) {
this.addDeclaration(xdocument.declaration);
}
for (const childXNode of xdocument.nodes()) {
this.xmlDocument.appendChild(this.createNode(childXNode));
}
return this.xmlDocument;
}
addDeclaration(declaration) {
let data = '';
if (declaration.version) {
data += ` version="${declaration.version}"`;
}
if (declaration.encoding) {
data += ` encoding="${declaration.encoding}"`;
}
if (declaration.standalone) {
data += ` standalone="${declaration.standalone}"`;
}
this.xmlDocument.appendChild(this.xmlDocument.createProcessingInstruction('xml', data.trimStart()));
}
createElement(xelement) {
// Register the namespaces declared by the element.
this.pushElement(xelement);
const element = this.constructElement(xelement);
for (const xattribute of xelement.attributes()) {
this.setAttribute(element, xattribute);
}
for (const childXNode of xelement.nodes()) {
element.appendChild(this.createNode(childXNode));
}
// Unregister the namespaces declared by the element.
this.resolver.popScope();
return element;
}
constructElement(xelement) {
const name = xelement.name;
return name.namespace === XNamespace.none
? this.xmlDocument.createElement(name.localName)
: this.xmlDocument.createElementNS(name.namespaceName, this.getQualifiedName(xelement));
}
setAttribute(element, xattribute) {
const name = xattribute.name;
if (name.namespace === XNamespace.none) {
if (name.localName === 'xmlns') {
// Translate from LINQ to DOM representation of default namespace
// declaration. In the DOM, default namespace declaration have a
// non-empty namespace name.
element.setAttributeNS(XNamespace.xmlnsPrefixNamespaceName, 'xmlns', xattribute.value);
}
else {
element.setAttribute(name.localName, xattribute.value);
}
}
else {
element.setAttributeNS(name.namespaceName, this.getQualifiedName(xattribute), xattribute.value);
}
}
createNode(xnode) {
if (xnode instanceof XElement) {
return this.createElement(xnode);
}
else if (xnode instanceof XText) {
return this.xmlDocument.createTextNode(xnode._text);
}
else if (xnode instanceof XProcessingInstruction) {
return this.xmlDocument.createProcessingInstruction(xnode.target, xnode.data);
}
else {
throw new ArgumentException('xnode', 'Unexpected kind of XNode');
}
}
getQualifiedName(namedObject) {
const name = namedObject._name;
const prefix = this.getPrefixOfNamespace(name.namespace, namedObject instanceof XElement);
return prefix ? `${prefix}:${name.localName}` : name.localName;
}
getPrefixOfNamespace(ns, allowDefaultNamespace) {
const namespaceName = ns.namespaceName;
if (namespaceName.length > 0) {
const prefix = this.resolver.getPrefixOfNamespace(ns, allowDefaultNamespace);
if (prefix !== null)
return prefix;
if (namespaceName === XNamespace.xmlPrefixNamespaceName)
return 'xml';
if (namespaceName === XNamespace.xmlnsPrefixNamespaceName)
return 'xmlns';
}
return null;
}
pushAncestors(e) {
while (e.parent instanceof XElement) {
e = e.parent;
let a = e._lastAttr;
if (a !== null) {
do {
a = a._next;
if (a.isNamespaceDeclaration) {
this.resolver.addFirst(a._name.namespaceName.length === 0 ? '' : a._name.localName, XNamespace.get(a._value));
}
} while (a !== e._lastAttr);
}
}
}
pushElement(e) {
this.resolver.pushScope();
let a = e._lastAttr;
if (a !== null) {
do {
a = a._next;
if (a.isNamespaceDeclaration) {
this.resolver.add(a._name.namespaceName.length === 0 ? '' : a._name.localName, XNamespace.get(a._value));
}
} while (a !== e._lastAttr);
}
}
}
//# sourceMappingURL=DomFactory.js.map