Grouping

"Christoph Naber [email protected]" <[email protected]>
Newsgroups gmane.text.xml.xsl.general.mulberrytech
Message-ID <[email protected]>
Hello xsl-list,

the question of Charlie brought me to thinking about a problem and solution
that I came up with a few years ago.
A few words upfront:
- This is a _very_ stripped down example which doesn't reflect the complete
structure. I hope that I was able to retain all relevant structural
attributes that shape the solution.
- I included <parentA> and <parentB> in the example to account for the fact
that there are at least 20 different parents where the relevant <points>
can occur as childs. It's also possible that a parent holds relevant
<points>, completely different nodes and also another parent.
- I don't want to include <xsl:for-each-group > in every possible parent
for the sake of maintainability. Because of the loads of different child
nodes it seems to me atm that it's not sensible to write specialized
select-statements in apply-templates. Just select="@* | node()" and let the
child-nodes "somehow" take care of themselves.

My solution is somewhat like the following
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" method="xml" encoding="UTF-8"
media-type="text/xml"/>

  <xsl:template match="parentA">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="parentB">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
    <special/>
  </xsl:template>

  <xsl:template match="@* | node()" priority="-1">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="point[@type='1']">
    <xsl:message>stripped point</xsl:message>
  </xsl:template>

  <!-- in the first position or directly after a node() that is not part of
the group -->
  <xsl:template match="point[@type='1' and ((position() = 1) or
(preceding-sibling::*[1][self::point[not(@type='1')] or
not(self::point)]))]"
    priority="10">
    <xsl:variable name="first-point-not-in-group"
select="following-sibling::*[self::point[not(@type='1')] or
not(self::point)][1]"/>
    <group>
      <!-- apply-templates to self and everything that follows until the
next point that is not part of the group -->
      <xsl:apply-templates select=". |
following-sibling::*[generate-id(self::*) ne
generate-id($first-point-not-in-group) and
      (every $i in preceding-sibling::point satisfies (generate-id($i) ne
generate-id($first-point-not-in-group)))]" mode="grouping"/>
    </group>
  </xsl:template>

  <xsl:template match="node()" mode="grouping">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

I would be interested in ways to tackle this. Are there patterns for this
type of grouping problem? Is it favorable to put "for-each-group" stmts all
over the place? In general: are there better ways to solve this?

The sample input:
<base>
  <parentA>
    <element />
    <point value="A-A" type="1" />
    <point value="A-B" type="1" />
    <point value="A-C" type="2" />
    <point value="A-D" type="1" />
    <point value="A-E" type="2" />
    <point value="A-F" type="1" />
    <point value="A-G" type="1" />
    <point value="A-H" type="1" />
  </parentA>
  <parentB>
    <point value="B-A" type="1" />
    <point value="B-B" type="1" />
    <point value="B-C" type="2" />
    <element />
    <point value="B-D" type="1" />
    <point value="B-E" type="1" />
  </parentB>
</base>

The expected output:

<?xml version="1.0" encoding="UTF-8"?>
<base>
  <parentA>
    <element/>
    <group>
      <point value="A-A" type="1"/>
      <point value="A-B" type="1"/>
    </group>
    <point value="A-C" type="2"/>
    <group>
      <point value="A-D" type="1"/>
    </group>
    <point value="A-E" type="2"/>
    <group>
      <point value="A-F" type="1"/>
      <point value="A-G" type="1"/>
      <point value="A-H" type="1"/>
    </group>
  </parentA>
  <parentB>
    <group>
      <point value="B-A" type="1"/>
      <point value="B-B" type="1"/>
    </group>
    <point value="B-C" type="2"/>
    <element/>
    <group>
      <point value="B-D" type="1"/>
      <point value="B-E" type="1"/>
    </group>
  </parentB>
  <special/>
</base>

Thank you in advance for any insight!

Best regards
Christoph Naber
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/3329386
or by email: [email protected]
--~--
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.