Skip to content

Commit e507eb8

Browse files
Solving cases where tag attributes were not correctly assigned to its respective output tags.
1 parent a37a6f3 commit e507eb8

File tree

1 file changed

+53
-27
lines changed

1 file changed

+53
-27
lines changed

src/xslt/xslt.ts

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export class Xslt {
125125
}
126126
}
127127

128-
this.xsltProcessContext(expressionContext, stylesheet, outputDocument);
128+
this.xsltProcessContext(expressionContext, stylesheet);
129129
const transformedOutputXml = xmlTransformedText(outputDocument, {
130130
cData: false,
131131
escape: this.options.escape,
@@ -136,11 +136,11 @@ export class Xslt {
136136

137137
/**
138138
* The main entry point of the XSL-T processor, as explained on the top of the file.
139-
* @param context The input document root, as XPath ExprContext.
139+
* @param context The input document root, as XPath `ExprContext`.
140140
* @param template The stylesheet document root, as DOM node.
141-
* @param output the root of the generated output, as DOM node.
141+
* @param output If set, the output where the transformation should occur.
142142
*/
143-
protected xsltProcessContext(context: ExprContext, template: XNode, output: XNode) {
143+
protected xsltProcessContext(context: ExprContext, template: XNode, output?: XNode) {
144144
if (!this.isXsltElement(template)) {
145145
this.xsltPassThrough(context, template, output);
146146
} else {
@@ -228,10 +228,28 @@ export class Xslt {
228228
case 'attribute':
229229
nameExpr = xmlGetAttribute(template, 'name');
230230
name = this.xsltAttributeValue(nameExpr, context);
231-
node = domCreateDocumentFragment(this.outputDocument);
232-
this.xsltChildNodes(context, template, node);
233-
value = xmlValue2(node);
234-
domSetTransformedAttribute(output, name, value);
231+
const documentFragment = domCreateDocumentFragment(this.outputDocument);
232+
this.xsltChildNodes(context, template, documentFragment);
233+
value = xmlValue2(documentFragment);
234+
if (output !== null && output !== undefined) {
235+
domSetTransformedAttribute(output, name, value);
236+
} else {
237+
let outputNode = context.outputNodeList[context.outputPosition];
238+
// Corner case:
239+
// It can happen here that we don't have the root node set.
240+
// In this case we need to append a copy of the root
241+
// source node to receive the attribute.
242+
if (outputNode.localName === "#document") {
243+
const sourceRootNode = context.root.childNodes[0];
244+
const newRootNode = domCreateElement(this.outputDocument, sourceRootNode.nodeName);
245+
newRootNode.transformedNodeName = sourceRootNode.nodeName;
246+
newRootNode.transformedLocalName = sourceRootNode.localName;
247+
domAppendTransformedChild(outputNode, newRootNode);
248+
outputNode = newRootNode;
249+
}
250+
domSetTransformedAttribute(outputNode, name, value);
251+
}
252+
235253
break;
236254
case 'attribute-set':
237255
throw new Error(`not implemented: ${template.localName}`);
@@ -265,22 +283,24 @@ export class Xslt {
265283
output.appendChild(commentNode);
266284
break;
267285
case 'copy':
268-
node = this.xsltCopy(output, context.nodeList[context.position]);
286+
const destinationCopyNode = output || context.outputNodeList[context.outputPosition];
287+
node = this.xsltCopy(destinationCopyNode, context.nodeList[context.position]);
269288
if (node) {
270289
this.xsltChildNodes(context, template, node);
271290
}
272291
break;
273292
case 'copy-of':
274293
select = xmlGetAttribute(template, 'select');
275294
value = this.xPath.xPathEval(select, context);
295+
const destinationNode = output || context.outputNodeList[context.outputPosition];
276296
if (value.type === 'node-set') {
277297
nodes = value.nodeSetValue();
278298
for (let i = 0; i < nodes.length; ++i) {
279-
this.xsltCopyOf(output, nodes[i]);
299+
this.xsltCopyOf(destinationNode, nodes[i]);
280300
}
281301
} else {
282302
let node = domCreateTextNode(this.outputDocument, value.stringValue());
283-
domAppendChild(output, node);
303+
domAppendChild(destinationNode, node);
284304
}
285305
break;
286306
case 'decimal-format':
@@ -397,15 +417,21 @@ export class Xslt {
397417
if (disableOutputEscaping.length > 0 && disableOutputEscaping[0].nodeValue === 'yes') {
398418
node.escape = false;
399419
}
400-
output.appendTransformedChild(node);
420+
const destinationTextNode = output || context.outputNodeList[context.outputPosition];
421+
destinationTextNode.appendTransformedChild(node);
401422
break;
402423
case 'value-of':
403424
select = xmlGetAttribute(template, 'select');
404425
const attribute = this.xPath.xPathEval(select, context);
405426
value = attribute.stringValue();
406427
node = domCreateTransformedTextNode(this.outputDocument, value);
407428
node.siblingPosition = context.nodeList[context.position].siblingPosition;
408-
context.outputNodeList[context.outputPosition].appendTransformedChild(node);
429+
if (output !== null && output !== undefined) {
430+
output.appendTransformedChild(node);
431+
} else {
432+
context.outputNodeList[context.outputPosition].appendTransformedChild(node);
433+
}
434+
409435
break;
410436
case 'variable':
411437
this.xsltVariable(context, template, true);
@@ -483,25 +509,25 @@ export class Xslt {
483509
* Implements `xsl:copy-of` for node-set values of the select
484510
* expression. Recurses down the source node tree, which is part of
485511
* the input document.
486-
* @param {XNode} dst the node being copied to, part of output document.
487-
* @param {XNode} src the node being copied, part in input document.
512+
* @param {XNode} destination the node being copied to, part of output document.
513+
* @param {XNode} source the node being copied, part in input document.
488514
*/
489-
protected xsltCopyOf(dst: XNode, src: XNode): void {
490-
if (src.nodeType == DOM_DOCUMENT_FRAGMENT_NODE || src.nodeType == DOM_DOCUMENT_NODE) {
491-
for (let i = 0; i < src.childNodes.length; ++i) {
492-
this.xsltCopyOf(dst, src.childNodes[i]);
515+
protected xsltCopyOf(destination: XNode, source: XNode): void {
516+
if (source.nodeType == DOM_DOCUMENT_FRAGMENT_NODE || source.nodeType == DOM_DOCUMENT_NODE) {
517+
for (let i = 0; i < source.childNodes.length; ++i) {
518+
this.xsltCopyOf(destination, source.childNodes[i]);
493519
}
494520
} else {
495-
const node = this.xsltCopy(dst, src);
521+
const node = this.xsltCopy(destination, source);
496522
if (node) {
497523
// This was an element node -- recurse to attributes and
498524
// children.
499-
for (let i = 0; i < src.attributes.length; ++i) {
500-
this.xsltCopyOf(node, src.attributes[i]);
525+
for (let i = 0; i < source.attributes.length; ++i) {
526+
this.xsltCopyOf(node, source.attributes[i]);
501527
}
502528

503-
for (let i = 0; i < src.childNodes.length; ++i) {
504-
this.xsltCopyOf(node, src.childNodes[i]);
529+
for (let i = 0; i < source.childNodes.length; ++i) {
530+
this.xsltCopyOf(node, source.childNodes[i]);
505531
}
506532
}
507533
}
@@ -633,9 +659,9 @@ export class Xslt {
633659
* current template node.
634660
* @param context Normally the Expression Context.
635661
* @param template The XSL-T definition.
636-
* @param output The XML output.
662+
* @param output If set, the output where the transformation should occur.
637663
*/
638-
protected xsltChildNodes(context: ExprContext, template: XNode, output: XNode) {
664+
protected xsltChildNodes(context: ExprContext, template: XNode, output?: XNode) {
639665
// Clone input context to keep variables declared here local to the
640666
// siblings of the children.
641667
const contextClone = context.clone();
@@ -720,7 +746,7 @@ export class Xslt {
720746
outputNode.transformedChildNodes.length - 1,
721747
++elementContext.outputDepth
722748
);
723-
this.xsltChildNodes(clonedContext, template, node);
749+
this.xsltChildNodes(clonedContext, template);
724750
} else {
725751
// This applies also to the DOCUMENT_NODE of the XSL stylesheet,
726752
// so we don't have to treat it specially.

0 commit comments

Comments
 (0)