Sorting XML Nodes Using ColdFusion And XSLT

<cffunction
	name="XmlSort"
	access="public"
	returntype="string"
	output="true"
	hint="I sort part of an XML documument based on the given XPath and sort characteristics.">
 
	<!--- Define arguments. --->
	<cfargument
		name="Xml"
		type="any"
		required="true"
		hint="I am an XML string or ColdFusion XML document."
		/>
 
	<cfargument
		name="ParentXPath"
		type="string"
		required="true"
		hint="I am the XPath to the PARENT node of the nodes which are targeted for sorting." />
 
	<cfargument
		name="SortXPath"
		type="any"
		required="false"
		default="text()"
		hint="I am the XPath value upon which the sort is being conducted. This can be a string or an array (if multiple sorting options are required)."
		/>
 
	<cfargument
		name="Direction"
		type="string"
		required="false"
		default="ascending"
		hint="I am the sort direction."
		/>
 
	<cfargument
		name="DataType"
		type="string"
		required="false"
		default="text"
		hint="I am the type of data that is being used in the sort (to help sorting)."
		/>
 
	<!--- Define the local scope. --->
	<cfset var LOCAL = {} />
 
 
	<!---
		Check to see if the given sorting option is a string or
		an array. If it's a string, then let's convert it to an
		array so that we can treat it uniformily later on.
	--->
	<cfif IsSimpleValue( ARGUMENTS.SortXPath )>
 
		<!---
			We need to copy this to get around a bug in the way
			ColdFusion handles implicit array creation involving
			its own values.
		--->
		<cfset LOCAL.SortCopy = ARGUMENTS.SortXPath />
 
		<!--- Convert simple value to an array. --->
		<cfset ARGUMENTS.SortXPath = [ LOCAL.SortCopy ] />
 
	</cfif>
 
 
	<!--- Define the XSL Transofrm data. --->
	<cfxml variable="LOCAL.Transform">
 
		<!--- Document type declaration. --->
		<?xml version="1.0" encoding="ISO-8859-1"?>
 
		<xsl:transform
			version="1.0"
			xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
			<!--- Match all generic nodes. --->
			<xsl:template match="*">
				<!--- Copy this node (non-deep copy). --->
				<xsl:copy>
					<!---
						Make sure that all attributes are copied
						over for the current node.
					--->
					<xsl:copy-of select="@*" />
 
					<!---
						Apply templates to all of it's child
						nodes (so that they can be copied).
					--->
					<xsl:apply-templates />
				</xsl:copy>
			</xsl:template>
 
 
			<!---
				Match the parent node of the target nodes.
				From here, we can copy the parent and then
				control how the child nodes are sorted.
			--->
			<xsl:template match="#ARGUMENTS.ParentXPath#">
 
				<!---
					Copy the current node's top-level values
					(the tag and it's attributes, but not it's
					descendents).
				--->
				<xsl:copy>
 
					<!---
						Make sure that all attributes are copied
						over for the current node.
					--->
					<xsl:copy-of select="@*" />
 
					<!--- Loop over the xmlitem nodes. --->
					<xsl:for-each select="*">
 
						<!--- Output all sorting options. --->
						<cfloop
							index="LOCAL.SortXPath"
							array="#ARGUMENTS.SortXPath#">
 
							<xsl:sort
								select="#LOCAL.SortXPath#"
								data-type="text"
								order="#ARGUMENTS.Direction#"
								/>
 
						</cfloop>
 
						<!---
							Copy the entire node (include its
							descendantas).
						--->
						<xsl:copy-of select="." />
 
					</xsl:for-each>
 
				</xsl:copy>
 
			</xsl:template>
 
		</xsl:transform>
 
	</cfxml>
 
 
	<!--- Return the tranformation. --->
	<cfreturn XmlTransform(
		ARGUMENTS.Xml,
		LOCAL.Transform
		) />
</cffunction>

For Cut-and-Paste