<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:mlang="http://mulberrytech.com/mlang">

<!--  A TABLE-DRIVEN XML NAME NORMALIZER

Sometimes SGML tools will do things that mess with tag names,
such as folding them uniformly into upper or lower case. This
is a pain because they must be switched back into their original
forms (their DTD-normalized forms) to be validated as XML, if you
want to move the file back into an XML environment. If their
proper forms happen to include mixed-case names, you're in a
hole. The perfect solution would be a DTD-aware process.
A less perfect approach is to provide the correct names in an
alternate spec of some sort. Since unextended XSLT has no
access to the DTD's model, this stylesheet takes the latter
approach.

This stylesheet is wired to run with a configuration file
'mlangnames.xml' as its listing of names (element names,
attribute names and names of enumerated attribute values);
but you can override that either by changing the stylesheet or
by setting by switching the 'names' parameter on the command
line, thus normalizing to the tag set of your choice.

So, for example, a routine invoking the Saxon processor could run:

Saxon -o fixed.xml messedup.xml xmlnames.xsl names=mynames.xml

See mlangnames.xml for the format of the names lists.

-->

<xsl:param name="names" select="'mlangnames.xml'"/>

<!-- The file name where the names table is held can be altered from
     the command line. -->

<xsl:variable name="elementnames" select="document($names)//elem/name"/>

<xsl:variable name="attributenames" select="document($names)//attr/name"/>

<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>

<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

<xsl:template match="processing-instruction()|comment()">
  <xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="*">
  <xsl:variable name="uppername"
       select="translate(name(), $lower, $upper)"/>
  <!-- variable $uppername is a version of the element name folded into
       upper case, for matching. -->
  <xsl:variable name="realname"
       select="$elementnames[$uppername = translate(., $lower, $upper)]"/>
  <!-- variable $realname is the normalized name for the element,
       as listed in the table. -->

  <!-- the action: either the element will be recreated with
       the normalized name, or it will be copied in (identity transform). -->
  <xsl:choose>
    <xsl:when test="$realname">
      <!-- when the lookup in the table of names has worked, create an element
           with the normalized name, and descend. -->
      <xsl:element name="{$realname}">
        <xsl:apply-templates select="@*|node()"/>
      </xsl:element>
    </xsl:when>
    <xsl:otherwise>
      <!-- otherwise, the lookup has failed, so copy the current element into
           the result as is, and descend. -->
      <xsl:message>
        <xsl:value-of select="concat('&lt;', name(), '&gt;')"/>
        <xsl:text> is not listed in the elements table and was not normalized.</xsl:text>
      </xsl:message>
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>

</xsl:template>

<xsl:template match="@*">

  <xsl:variable name="uppername"
       select="translate(name(), $lower, $upper)"/>
  <!-- variable $uppername is a version of the element name folded into
       upper case, for matching. -->

  <xsl:variable name="realname"
       select="$attributenames[$uppername = translate(., $lower, $upper)]"/>
  <!-- variable $realname is the normalized name for the attribute,
       as listed in the table. -->

  <xsl:variable name="uppervalue"
       select="translate(., $lower, $upper)"/>
  <!-- variable $uppervalue is a version of the attribute value folded into
       upper case, for matching. -->

  <xsl:variable name="values" select="$realname/../value"/>
  <!-- variable $values is a list of possible values associated with the
       attribute, also to be normalized (i.e. enumerated attribute values). -->

  <xsl:variable name="realvalue"
       select="$values[$uppervalue = translate(., $lower, $upper)]"/>
  <!-- variable $realvalue is the normalized value for the attribute,
       if listed in the table. -->

  <!-- the action: either the attribute will be recreated with
       the normalized name and a normalized value (if one exists; otherwise
       the value will be preserved); or it will be copied in as it stands
       (identity transform). -->
  <xsl:choose>
    <xsl:when test="$realname">
      <!-- when the lookup in the table of names has worked, create an attribute
           with the normalized name, and descend. -->
      <xsl:attribute name="{$realname}">
        <xsl:choose>
          <xsl:when test="string($realvalue)">
            <xsl:value-of select="$realvalue"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="."/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
    </xsl:when>
    <xsl:otherwise>
      <!-- otherwise, the lookup has failed, so copy the current attribute into
           the result as is, and descend. -->
      <xsl:message>
        <xsl:value-of select="concat('@', name())"/>
        <xsl:text> is not listed in the attributes table and was not normalized.</xsl:text>
      </xsl:message>
      <xsl:copy-of select="."/>
    </xsl:otherwise>
  </xsl:choose>

</xsl:template>

</xsl:stylesheet>