<?xml version="1.0" encoding="UTF-8"?>
<!-- 
	camelcase.xslt
	Author: Tom Underhill <tomun@microsoft.com>
	Transforms an XML file to a camelCased file.
	Simply sets the first character of each element name, attribute name, and attribute value to lower case.
	
	9-8-04	tomun	Created.
	9-23-04	tomun	Added support to pretty print as it camelCases.
	9-24-04	tomun	Added support to correctly output namespace declarations.
-->
<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="no"/>
	
	<!-- global variables -->
	<xsl:variable name="upper">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
	<xsl:variable name="lower">abcdefghijklmnopqrstuvwxyz</xsl:variable>

	<!-- text or any other node types -->
	<xsl:template match="text() | node()">
		<xsl:copy/>
	</xsl:template>

	<!-- comments or pi's -->
	<xsl:template match="comment() | processing-instruction()">
		<xsl:param name="tabs"/>
		
		<!-- output a new line and indenting tabs -->
		<xsl:text>&#x0a;</xsl:text>
		<xsl:value-of select="$tabs"/>
		
		<!-- output the comments or pi -->
		<xsl:copy/>
	</xsl:template>

	<!-- attributes -->
	<xsl:template match="@*">
		<!-- output a space -->
		<xsl:text> </xsl:text>
		<!-- output the attribute name -->
		<xsl:value-of select="concat( translate(substring(name(),1,1), $upper, $lower), substring(name(), 2) )"/>
		<!-- output the ' =" ' -->
		<xsl:text>=</xsl:text>
		<xsl:choose>
			<!-- does the attribute value contain a quote char? -->
			<xsl:when test="contains(., '&quot;')">
				<xsl:text disable-output-escaping="yes">&apos;</xsl:text>
			</xsl:when>
			<xsl:otherwise>
				<xsl:text disable-output-escaping="yes">&quot;</xsl:text>
			</xsl:otherwise>
		</xsl:choose>
		<!-- output the attribute value -->
		<xsl:value-of select="concat( translate(substring(.,1,1), $upper, $lower), substring(., 2) )"/>
		<!-- output the closing quote -->
		<xsl:choose>
			<!-- does the attribute value contain a quote char? -->
			<xsl:when test="contains(., '&quot;')">
				<xsl:text disable-output-escaping="yes">&apos;</xsl:text>
			</xsl:when>
			<xsl:otherwise>
				<xsl:text disable-output-escaping="yes">&quot;</xsl:text>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<!-- elements -->
	<xsl:template match="*">
		<xsl:param name="tabs"/>
		<xsl:param name="nsDecls"/>
		
		<!-- output a new line and indenting tabs -->
		<xsl:text>&#x0a;</xsl:text>
		<xsl:value-of select="$tabs"/>
		
		<!-- output the less-than sign and the element name -->
		<xsl:text disable-output-escaping="yes">&lt;</xsl:text>
		<xsl:value-of select="concat( translate(substring(name(.),1,1), $upper, $lower), substring(name(.), 2) )"/>

		<!-- output the namespace declaration attributes filtering out ones already declared by ancestors -->
		<xsl:for-each select="namespace::node()">
			<!-- put each namespace decl into a variable -->
			<xsl:variable name="nsDecl">
				<xsl:if test="name() != 'xml'">
					<!-- output a space and then xmlns -->
					<xsl:text> xmlns</xsl:text>
					<xsl:if test="name() != ''">
						<!-- output a colon -->
						<xsl:text>:</xsl:text>
						<!-- output the prefix name -->
						<xsl:value-of select="name()"/>
					</xsl:if>						
					<!-- output the ' =" ' -->
					<xsl:text disable-output-escaping="yes">=&quot;</xsl:text>
					<!-- output the namespace uri -->
					<xsl:value-of select="."/>
					<!-- output the closing quote -->
					<xsl:text disable-output-escaping="yes">&quot;</xsl:text>
				</xsl:if>
			</xsl:variable>
			<!-- output only if not already declared by an ancestor -->
			<xsl:if test="not(contains($nsDecls, $nsDecl))">
				<xsl:value-of select="$nsDecl"/>
			</xsl:if>				
		</xsl:for-each>

		<!-- output the attributes -->
		<xsl:apply-templates select="@*"/>

		<xsl:choose>
			<!-- does this element have children or text? -->
			<xsl:when  test="count(* | node())">
				<!-- output the closing greater-than sign -->	
				<xsl:text disable-output-escaping="yes">&gt;</xsl:text> 

				<!-- make a variable that contains *all* the namespace declaration for this element -->
				<xsl:variable name="curNsDecls">
					<xsl:for-each select="namespace::node()">
						<xsl:if test="name() != 'xml'">
							<!-- output a space and then xmlns -->
							<xsl:text> xmlns</xsl:text>
							<xsl:if test="name() != ''">
								<!-- output a colon -->
								<xsl:text>:</xsl:text>
								<!-- output the prefix name -->
								<xsl:value-of select="name()"/>
							</xsl:if>						
							<!-- output the ' =" ' -->
							<xsl:text disable-output-escaping="yes">=&quot;</xsl:text>
							<!-- output the namespace uri -->
							<xsl:value-of select="."/>
							<!-- output the closing quote -->
							<xsl:text disable-output-escaping="yes">&quot;</xsl:text>
						</xsl:if>
					</xsl:for-each>
				</xsl:variable>

				<!-- output the child element and/or text -->
				<xsl:apply-templates select="node()">
					<xsl:with-param name="tabs" select="concat($tabs, '&#x09;')"/>
					<xsl:with-param name="nsDecls" select="$curNsDecls"/>
				</xsl:apply-templates>

				<!-- were child elements outputted above? -->
				<xsl:if test="count(*)">
					<!-- output a new line and indenting tabs -->
					<xsl:text>&#x0a;</xsl:text>
					<xsl:value-of select="$tabs"/>
				</xsl:if>

				<!-- output the closing element tag -->
				<xsl:text disable-output-escaping="yes">&lt;/</xsl:text>
				<xsl:value-of select="concat( translate(substring(name(.),1,1), $upper, $lower), substring(name(.), 2) )"/>
				<xsl:text disable-output-escaping="yes">&gt;</xsl:text> 
			</xsl:when>
			<xsl:otherwise>
				<!-- output the closing slash and greater-than sign -->	
				<xsl:text disable-output-escaping="yes">/&gt;</xsl:text> 
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<!-- document root -->
	<xsl:template match="/" >
		<xsl:apply-templates select="@* | node()"/>
	</xsl:template>
</xsl:stylesheet>
