Hi Ethan,
I added commented patches below..
Cheers,
Geert
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<!-- xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl" -->
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- keys for grouping -->
<xsl:key name="cities" match="cities" use="city[(_at_)lang='en']"/>
<xsl:key name="offices" match="office"
use="concat(cities/city[(_at_)lang='en'],'-',names/name[(_at_)lang='en'])"/>
For the index using solution, add:
<xsl:key name="offices-by-country" match="offices" use="../@name"/>
<xsl:template match="/">
<xsl:comment> with apply-templates </xsl:comment>
<xsl:apply-templates/>
</xsl:template>
<!-- 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 within this country
-->
<xsl:variable name="offices-in-this-country">
<xsl:copy-of select="offices"/>
<!-- this is a trick, to restrict all
subsequent key usage to this country only -->
</xsl:variable>
<xsl:apply-templates
select="msxsl:node-set($offices-in-this-country)/offices/office/cities[count(.|key('cities',city[(_at_)lang='en'])[1])=1]">
<xsl:sort
select="city[(_at_)lang='en']"/>
</xsl:apply-templates>
I wonder if something like:
<xsl:variable name="offices-in-this-country" select="offices" />
wouldn't have worked just as fine.
Note that the select of apply-templates should be changed as well into:
<xsl:apply-templates
select="$offices-in-this-country/office
/cities[count(. | key('cities', city[(_at_)lang='en'])[1]) = 1]">
...
</xsl:apply-templates>
However, this is not the suggestion I tried to make. I said 'why not use an index'. Then, you will
no longer need the variable but only the index I added at the top. Change apply-templates into:
<xsl:apply-templates
select="key('offices-by-country', @name)/office
/cities[count(. | key('cities', city[(_at_)lang='en'])[1]) = 1]">
...
</xsl:apply-templates>
</cities>
</country>
</xsl:template>
<!-- new 'city' elements -->
<xsl:template match="cities">
<city name="{city[(_at_)lang='en']}">
<!-- (uncomment this if you need multilingual city names in the
output)
<city>
<xsl:apply-templates select="city" mode="name"/>
-->
<offices>
<!-- group 'office' elements located in this city
by their english name -->
<xsl:variable name="this-city"
select="city[(_at_)lang='en']"/>
<xsl:variable name="offices-in-this-city"
select="../../../offices/office[cities/city[(_at_)lang='en']=$this-city]"/>
<xsl:apply-templates
select="$offices-in-this-city
[count(.|key('offices',
concat(cities/city[(_at_)lang='en'],'-',names/name[(_at_)lang='en']))[1])=1]">
<xsl:sort
select="names/name[(_at_)lang='en']"/>
</xsl:apply-templates>
</offices>
</city>
</xsl:template>
<!-- new city name elements -->
<xsl:template match="city" mode="name">
<name>
<xsl:copy-of select="@*"/>
<xsl:value-of select="."/>
</name>
</xsl:template>
<!-- new office elements -->
<xsl:template match="office">
<office>
<xsl:apply-templates select="names/name"/>
<!-- add locations for each office with this name -->
<xsl:apply-templates select="key('offices',
concat(cities/city[(_at_)lang='en'],'-',names/name[(_at_)lang='en']))"
mode="location">
<xsl:sort select="address"/>
</xsl:apply-templates>
</office>
</xsl:template>
<!-- new location elements -->
<xsl:template match="office" mode="location">
<location>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="address|phone"/>
</location>
</xsl:template>
</xsl:stylesheet>