Reputation: 658
I want to change the processing order a list of elements based on a dependency in the children of other elements.
Simplified example XML:
<root>
<elem name="a">
<dependency name="b"/>
<dependency name="c"/>
</elem>
<elem name="b">
<dependency name="x"/>
<dependency name="d"/>
</elem>
<elem name="c">
<dependency name="e"/>
</elem>
<elem name="d">
<dependency name="y"/>
<dependency name="e"/>
</elem>
<elem name="e">
<dependency name="x"/>
</elem>
</root>
The order in which I want to process them is the following:
<root>
<elem name="e">
<dependency name="x"/>
<elem name="c">
<dependency name="e"/>
</elem>
<elem name="d">
<dependency name="y"/>
<dependency name="e"/>
</elem>
<elem name="b">
<dependency name="x"/>
<dependency name="d"/>
</elem>
<elem name="a">
<dependency name="b"/>
<dependency name="c"/>
</elem>
</root>
The order of the elements c
and d
that both depend on e
is not important. Document order is fine, but sorted is ok too.
Any dependency for which there is no elem
element can be ignored in ordering, like x
and y
I have been trying several example stylesheets that seemed to do similar things, but I have not succeeded. It has been a while since I last used XSLT (unfortunately), and right now I am kind of stuck at this. A little bit of help would be appreciated. I do not request complete stylesheets for this, but snippets to get me going in the right direction would be highly appreciated.
Upvotes: 0
Views: 105
Reputation: 167716
I have written a function which tries to collect the elem
elements based on the dependencies:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="mf">
<xsl:output indent="yes"/>
<xsl:key name="name" match="elem" use="@name"/>
<xsl:key name="dep" match="elem" use="dependency/@name[key('name', .)]"/>
<xsl:variable name="main-doc" select="/"/>
<xsl:variable name="all-elements" select="root/elem"/>
<xsl:function name="mf:collect" as="element(elem)*">
<xsl:param name="elements-of-level" as="element(elem)*"/>
<xsl:param name="collected-elements" as="element(elem)*"/>
<xsl:variable name="deps"
select="key('dep', $elements-of-level/@name, $main-doc)
[not(. intersect $collected-elements)
and not(dependency/@name = ($all-elements except ($elements-of-level, $collected-elements))/@name)]"/>
<xsl:sequence select="if (not($deps)) then $collected-elements
else mf:collect($deps, ($collected-elements, $deps))"/>
</xsl:function>
<xsl:template match="root">
<xsl:copy>
<xsl:variable name="start" select="elem[not(dependency/@name = //elem/@name)]"/>
<xsl:sequence select="mf:collect($start, $start)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
For the input
<root>
<elem name="a">
<dependency name="b"/>
<dependency name="c"/>
</elem>
<elem name="b">
<dependency name="x"/>
<dependency name="d"/>
</elem>
<elem name="c">
<dependency name="e"/>
</elem>
<elem name="d">
<dependency name="y"/>
<dependency name="e"/>
</elem>
<elem name="e">
<dependency name="x"/>
</elem>
</root>
Saxon 9.5 gives me the output
<root>
<elem name="e">
<dependency name="x"/>
</elem>
<elem name="c">
<dependency name="e"/>
</elem>
<elem name="d">
<dependency name="y"/>
<dependency name="e"/>
</elem>
<elem name="b">
<dependency name="x"/>
<dependency name="d"/>
</elem>
<elem name="a">
<dependency name="b"/>
<dependency name="c"/>
</elem>
</root>
Upvotes: 1