<?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" 
	xmlns:user="http://www.sunaptic.com/xslt" 
	exclude-result-prefixes="xsl msxsl user" >

	<!-- **********************************************************************
	Stylesheet Title: BizTalk Map Documenter (HTML Output)
	Description:
	This stylesheet is designed to generate an XHTML file that documents a 
	BizTalk 2004 map (.btm file).  
	*********************************************************************** -->

	<!-- Want XHTML output. -->
	<xsl:output 
		method="xml" 
		indent="yes" 
		doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" 
		doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" 
		encoding="iso-8859-1" />

	<!--============================================================
	Main Template Entry Point
	=============================================================-->
	<xsl:template match="/">

		<xsl:variable name="destSchema" select="/mapsource/TrgTree/Reference/@Location" />

		<html>
		<head>
			<title>Map To <xsl:value-of select="$destSchema"/></title>
			<style type="text/css">
	body { font-family: Tahoma, Arial }
	hr { border: 0; height: 2px; width: 100%; align: center }
	.titleArea { border-left: 5px solid #333366; padding-left: 5px }
	#pageTitle { color: #0A2F66; font-size: 20px; font-weight: bold }
	.titleSchema { color: #FB7013; font-size: 14px; font-weight: bold }
	.linkTableTitle { color: #0A2F66; font-family: Verdana; font-size: 14px; font-weight: bold }
	.linkTable { font-size: 11px }
	.linkTableHeaderRow { font-family: Verdana; font-size: 11px; font-weight: bold }
	.linkTableAltRow { background-color: #FFF5C8 }
	.linkTableDiffNodeRow { background-color: #C8E2FF }
	.linkTableCol { border-bottom: 1px solid black; background-color: #CCCC99 }
	.functoidArea { font-family: Lucida Console, Courier New; font-size: 11px }
	.functoidName { color: blue }
	.functoidBracket { color: green; font-weight: bold }
	.functoidScript { color: #003300 }
	.paramComma { color: blue; font-weight: bold }
	.internalLink { font-size: 10px }
			</style>
		</head>

		<body>
		<a name="top"></a>
		<div class="titleArea">
			<span id="pageTitle">BizTalk 2004 Map Documentation</span>
			<table border="0">
			<tr class="titleSchema">
				<td>From Schema(s):</td>
				<td>
				    <xsl:choose>
				        <xsl:when test="/mapsource/SrcTree/Reference">
				            <xsl:value-of select="/mapsource/SrcTree/Reference/@Location"/>
				        </xsl:when>
				        <xsl:when test="/mapsource/SrcTree/*[local-name()='schema']">
				            <xsl:for-each select="/mapsource/SrcTree/*[local-name()='schema']/*[local-name()='import']">
				                <xsl:if test="position() &gt; 1">
				                    <xsl:text>, </xsl:text>
				                </xsl:if>
				                <xsl:value-of select="@schemaLocation"/>
				            </xsl:for-each>
				        </xsl:when>
				        <xsl:otherwise>
				            Unable to determine source schema(s). Update stylesheet to correct this issue.
				        </xsl:otherwise>
				    </xsl:choose>
				</td>
			</tr>
			<tr class="titleSchema">
				<td>To Schema:</td><td><xsl:value-of select="$destSchema"/></td>
			</tr>
			</table>
		</div>
        <a href="#functoids" class="internalLink">Functoids</a>
		<hr/>
		
		<!-- Process straight links (i.e. no functoid calls). -->
		<span class="linkTableTitle">Direct Node-to-Node Links (No Functoids)</span>
		<table border="0" class="linkTable">
		
			<tr class="linkTableHeaderRow">
				<td class="linkTableCol">Link (from -> to)</td>
			</tr>
			
			<xsl:apply-templates select="//Links/Link[not( number(@LinkFrom) or number(@LinkTo) )]" />
		</table>
		
		<hr/>
        <a name="functoids"></a>
		<span style="font-size: 12px">^&#160;</span><a href="#top" class="internalLink">Top</a>
		<br/><br/>
		
		<!-- Now process the functoid links. -->
		<span class="linkTabletitle">Functoid Mappings</span>
		<table border="0" class="linkTable">
			<tr class="linkTableHeaderRow">
				<td class="linkTableCol">Functoid Sequence</td>
				<td></td>
				<td class="linkTableCol">Destination Node</td>
			</tr>
			<xsl:for-each select="//Links/Link[number(@LinkFrom) &gt; 0 and not( number(@LinkTo) )]">
				<xsl:variable name="linkFrom" select="@LinkFrom" />
				<xsl:if test="position() mod 2 = 0">
					<tr class="linkTableAltRow">
						<td>
							<div class="functoidArea">
							<xsl:call-template name="ProcessFunctoid">
								<xsl:with-param name="functoidNode" select="//Functoids/Functoid[@FunctoidID = $linkFrom]" />
								<xsl:with-param name="callLevel" select="number(1)" />
							</xsl:call-template>
							</div>
						</td>
						<td style="font-size: 12px" nowrap="nowrap">--></td>
						<td>
							<xsl:value-of select="user:parseLinkPath( string(@LinkTo) )" />
						</td>
					</tr>
				</xsl:if>
				<xsl:if test="not( position() mod 2 = 0 )">
					<tr>
						<td>
							<div class="functoidArea">
							<xsl:call-template name="ProcessFunctoid">
								<xsl:with-param name="functoidNode" select="//Functoids/Functoid[@FunctoidID = $linkFrom]" />
								<xsl:with-param name="callLevel" select="number(1)" />
							</xsl:call-template>
							</div>
						</td>
						<td style="font-size: 12px">--></td>
						<td>
							<xsl:value-of select="user:parseLinkPath( string(@LinkTo) )" />
						</td>
					</tr>
				</xsl:if>
			</xsl:for-each>
		</table>
		
		</body>
		</html>
	</xsl:template>

	<!--======================================================================
	Template: Mapping Link
	=======================================================================-->
	<xsl:template match="Link">
		<xsl:variable name="linkFrom" select="user:parseLinkPath( string(@LinkFrom) )" />
		<xsl:variable name="linkTo" select="user:parseLinkPath( string(@LinkTo) )" />
		
		<xsl:variable name="isHL7NodeDiffLink" select="user:isHL7NodeDiffLink( $linkFrom, $linkTo )" />
		
		<xsl:choose>
			<xsl:when test="$isHL7NodeDiffLink">
				<tr class="linkTableDiffNodeRow">
					<td>
						<xsl:value-of select="$linkFrom" /><xsl:text>&#160;&#160;</xsl:text>
						<span style="font-size: 12px">--></span><br/>
						<xsl:value-of select="$linkTo" />
					</td>
				</tr>
			</xsl:when>
			
			<xsl:when test="position() mod 2 = 0">
				<tr class="linkTableAltRow">
					<td>
						<xsl:value-of select="$linkFrom" /><xsl:text>&#160;&#160;</xsl:text>
						<span style="font-size: 12px">--></span><br/>
						<xsl:value-of select="$linkTo" />
					</td>
				</tr>
			</xsl:when>
			
			<xsl:otherwise>
				<tr>
					<td>
						<xsl:value-of select="$linkFrom" /><xsl:text>&#160;&#160;</xsl:text>
						<span style="font-size: 12px">--></span><br/>
						<xsl:value-of select="$linkTo" />
					</td>
				</tr>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	
	<!--======================================================================
	Template: ProcessFunctoid
	Description:
	Works from destination node back through all connected functoids
	making recursive calls to this template when another functoid is 
	encountered.
	=======================================================================-->
	<xsl:template name="ProcessFunctoid">
		<xsl:param name="functoidNode" />
		<xsl:param name="callLevel" />

		<xsl:variable name="numberOfSpaces" select="number( ($callLevel - 1) * 3 )" />

		<xsl:value-of select="user:getHtmlSpaces( $numberOfSpaces )" disable-output-escaping="yes" />
		
		<span class="functoidName"><xsl:value-of select="$functoidNode/@Functoid-Name" /></span>

		<!-- Spit out all script code. -->
		<xsl:for-each select="$functoidNode/ScripterCode/Script">
			<span class="functoidScript">
			<xsl:value-of select="user:getHtmlSpaces( $numberOfSpaces )" disable-output-escaping="yes" />
			<xsl:choose>
				<xsl:when test="@Language = 'ExternalAssembly'">
					<br/>
					<xsl:value-of select="concat( @Class, '.', @Function )" />
				</xsl:when>
				<xsl:otherwise>
					<br/>
					<xsl:value-of select="." />
				</xsl:otherwise>
			</xsl:choose>
			</span>
		</xsl:for-each>

		<xsl:variable name="paramCount" select="count($functoidNode/Input-Parameters/Parameter)" />

		<!-- Starting functoid bracket. -->
		<xsl:choose>
			<xsl:when test="$paramCount = 1">
				<span class="functoidBracket"><xsl:value-of select="'( '" /></span>
			</xsl:when>
			<xsl:when test="$paramCount &gt; 1">
				<br/>
				<xsl:value-of select="user:getHtmlSpaces( $numberOfSpaces )" disable-output-escaping="yes" />
				<span class="functoidBracket"><xsl:value-of select="'('" /></span>
				<br/>
			</xsl:when>
		</xsl:choose>		

		<!-- List the functoid parameters. -->		
		<xsl:for-each select="$functoidNode/Input-Parameters/Parameter">
			<xsl:choose>
				<xsl:when test="@Type = 'Link'">
					<xsl:variable name="linkId" select="@Value" />
					<xsl:variable name="linkFrom" select="//Links/Link[@LinkID = $linkId]/@LinkFrom" />
					<xsl:variable name="linkTo" select="//Links/Link[@LinkID = $linkId]/@LinkTo" />
					<xsl:choose>
						<xsl:when test="number($linkFrom)">
							<!-- Parameter is a link from another functoid; apply functoid template recursively. -->
							<xsl:call-template name="ProcessFunctoid">
								<xsl:with-param name="functoidNode" select="//Functoids/Functoid[@FunctoidID = $linkFrom]" />
								<xsl:with-param name="callLevel" select="number( $callLevel + 1 )" />
							</xsl:call-template>
						</xsl:when>
						<xsl:otherwise>
							<!-- Parameter is a path; list it. -->
							<xsl:if test="$paramCount &gt; 1">
								<!-- When more than 1 parameter, parameters are indented. -->
								<xsl:value-of select="user:getHtmlSpaces( $numberOfSpaces + 3 )" disable-output-escaping="yes" />
							</xsl:if>
							<xsl:value-of select="user:parseLinkPath( string($linkFrom) )" />
						</xsl:otherwise>
					</xsl:choose>
				</xsl:when>
				<xsl:when test="@Type = 'Constant'">
					<xsl:value-of select="concat( '&quot;', @Value, '&quot;' )" />
				</xsl:when>
			</xsl:choose>
			<xsl:if test="position() &lt; $paramCount">
				<span class="paramComma">,</span><br/>
			</xsl:if>
		</xsl:for-each>				

		<!-- Closing functoid bracket. -->
		<xsl:choose>
			<xsl:when test="$paramCount = 1">
				<span class="functoidBracket"><xsl:value-of select="' )'"/></span>
			</xsl:when>
			<xsl:when test="$paramCount &gt; 1">
				<br/>
				<xsl:value-of select="user:getHtmlSpaces( $numberOfSpaces )" disable-output-escaping="yes" />
				<span class="functoidBracket"><xsl:value-of select="')'"/></span>
			</xsl:when>
		</xsl:choose>

	</xsl:template>

	<!--======================================================================
	Custom script functions used to provide functionality that cannot be 
	achieved (or not easily achieved) using XPath 1.0 functions.
	=======================================================================-->
	<msxsl:script language="JScript" implements-prefix="user">
	<![CDATA[
	// Parses a supplied link path returning a the path without the clutter of the
	// "local-name()" XPath statements.
	//
	function parseLinkPath( linkPath ) {
		// We want to parse out all the local-name() nodes, excluding what appears to 
		// be the standard first node ('Schema') for BizTalk 2004 maps.
		//
		var parsedLink = "";
		var nodeName = "";
		var startPos = -1;
		var endPos = -1;
		var nodeCount = 0;
		
		startPos = linkPath.indexOf( "'", 0 );
		
		// Find all single quoted nodes, extract them and put them back together in 
		// a path which excludes all the "local-name()=" xpath stuff.  There's probably
		// an easier way of doing this (like, perhaps, converting the string to an XPath
		// statement and using XPath-related methods to "clean" the path)?
		//
		while ( startPos > -1 ) {
			nodeCount++;
			endPos = linkPath.indexOf( "'", startPos+1 );
			if ( endPos > -1 ) {
				nodeName = linkPath.substring( startPos+1, endPos );

				// Ignore first node named 'Schema' which seems to be the standard (in BizTalk 2004) 
				// first node in the path for all maps?
				//
				if ( (nodeCount > 1) || (nodeName != "<Schema>") ) {
					if ( parsedLink.length > 0 ) {
						parsedLink += "/";
					}
					parsedLink += nodeName;
				}
			}
			startPos = linkPath.indexOf( "'", endPos+1 );
		}
		if ( parsedLink.length == 0 ) {
			// Just in case nothing was parsed from the supplied link path.
			//
			parsedLink = linkPath;
		}
		return parsedLink;
	}

	// Determines if the source and destination links are HL7 links and if the
	// linked nodes are different.
	//
	function isHL7NodeDiffLink( fromLink, toLink ) {
		var isDiffNodeLink = false;
		var fromLinkIsHL7 = false;
		var toLinkIsHL7 = false;
		var fromLinkNodes = fromLink.split( "/" );
		var toLinkNodes = toLink.split( "/" );
		if ( (fromLinkNodes[0].search(/GLO_DEF$/) > -1) && (toLinkNodes[0].search(/GLO_DEF$/) > -1) ) {
			var fromNode = fromLinkNodes[ fromLinkNodes.length - 1 ];
			var toNode = toLinkNodes[ toLinkNodes.length - 1 ];
			// From observations so far, it appears that all the schema node names generated/created 
			// via the HL7 accelerator have an underscore following the field/segment identifier.
			var fromNodeId = fromNode.substring( 0, fromNode.indexOf("_") );
			var toNodeId = toNode.substring( 0, toNode.indexOf("_") );
			if ( fromNodeId != toNodeId ) {
				isDiffNodeLink = true;
			}
		}
		return isDiffNodeLink;
	}
	
	// Builds a string of HTML non-breaking spaces.
	//	
	function getHtmlSpaces( spacesCount ) {
		var spaces = "";
		for ( var i = 0; i < spacesCount; i++ ) {
			spaces += "&nbsp;";
		}
		return spaces;
	}
	]]>
	</msxsl:script>

</xsl:stylesheet>
