Hi Ethan,
Here's a stylesheet that produces the desired output, by using the
identity transform
to copy all elements, and additional templates to match the elements you
want to change:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- keys by city name -->
<xsl:key name="cities" match="city" use="."/>
<xsl:key name="offices" match="office" use="cities/city"/>
<!-- identity transform: copy all elements -->
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- sort divisions by @id -->
<xsl:template match="divisions">
<divisions>
<xsl:apply-templates select="division">
<xsl:sort select="@id"/>
</xsl:apply-templates>
</divisions>
</xsl:template>
<!-- sort regions and countries by @name -->
<xsl:template match="regions|countries">
<xsl:copy>
<xsl:apply-templates select="region|country">
<xsl:sort select="@name"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!-- reorganize 'country' elements -->
<xsl:template match="country">
<country>
<xsl:copy-of select="@*"/>
<cities>
<!-- group 'city' elements -->
<xsl:apply-templates
select="offices/office/cities/city[count(.|key('cities',.)[1])=1]">
<xsl:sort select="."/>
</xsl:apply-templates>
</cities>
</country>
</xsl:template>
<!-- new 'city' elements -->
<xsl:template match="city">
<city>
<name><xsl:value-of select="."/></name>
<offices>
<xsl:apply-templates select="key('offices',.)">
<xsl:sort select="name"/>
</xsl:apply-templates>
</offices>
</city>
</xsl:template>
<!-- new office elements -->
<xsl:template match="office">
<office>
<xsl:copy-of select="@*"/>
<!-- copy all children except 'cities' -->
<xsl:apply-templates select="*[local-name()!='cities']"/>
</office>
</xsl:template>
</xsl:stylesheet>
Note however, the Muenchian grouping will only work properly if there
are no cities
with the same name in different countries (the 'cities' key matches all
city elements
throughout the document). If that's a problem you'll have to include
additional
information in the key's 'use' value, something like:
<xsl:key name="cities" match="city"
use="concat(ancestor::country/@name,'-',.)"/>
and then of course, use the same test in the Muenchian grouping predicate:
[count(.|key('cities',concat(ancestor::country/@name,'-',.))[1])=1]
Cheers,
Anton