Peter Hofman
Peter Hofman

Reputation: 658

how to order elements with dependency in children

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

Answers (1)

Martin Honnen
Martin Honnen

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

Related Questions