<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
  xmlns="urn:nhibernate-mapping-2.2"
  xmlns:adl="http://cygnets.co.uk/schemas/adl-1.2"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<!--
      Application Description Framework
      adl2hibernate.xsl
      
      (c) 2007 Cygnet Solutions Ltd
      
      Transform ADL to Hibernate
      
      $Author: sb $
      $Revision: 1.11 $
  -->

	<xsl:param name="namespace"/>
	<xsl:param name="assembly"/>
	<xsl:param name="database"/>
	<!-- the name and version of the product being built -->
	<xsl:param name="product-version" select="'Application Description Language Framework'"/>

	<xsl:output indent="no" method="xml" encoding="UTF-8"/>
	<!-- NOTE! indent="no" because hibernate falls over if there is whitespace inside
    a 'key' or 'one-to-many' element, and the printer used by the NAnt 'style' task
    does not tag-minimize on output. If you change this the build will break, you
    have been warned! -->

	<xsl:include href="csharp-type-include.xslt"/>

	<xsl:variable name="dbprefix">
		<xsl:choose>
			<xsl:when test="string-length( $database) > 0">
				<xsl:value-of select="concat( $database, '.dbo.')"/>
			</xsl:when>
			<xsl:otherwise/>
		</xsl:choose>
	</xsl:variable>

	<!-- define upper and lower case letters to enable case conversion -->
	<xsl:variable name="ucase">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
	<xsl:variable name="lcase">abcdefghijklmnopqrstuvwxyz</xsl:variable>
	<!-- define SQL keywords to police these out of field names -->
	<xsl:variable name="sqlkeywords-multiline">
		ADD 				EXCEPT 				PERCENT
		ALL 				EXEC 				PLAN
		ALTER 				EXECUTE				PRECISION
		AND 				EXISTS 				PRIMARY
		ANY 				EXIT 				PRINT
		AS 					FETCH 				PROC
		ASC 				FILE 				PROCEDURE
		AUTHORIZATION 		FILLFACTOR 			PUBLIC
		BACKUP 				FOR 				RAISERROR
		BEGIN 				FOREIGN 			READ
		BETWEEN 			FREETEXT 			READTEXT
		BREAK 				FREETEXTTABLE 		RECONFIGURE
		BROWSE 				FROM 				REFERENCES
		BULK 				FULL 				REPLICATION
		BY 					FUNCTION 			RESTORE
		CASCADE 			GOTO 				RESTRICT
		CASE 				GRANT 				RETURN
		CHECK 				GROUP 				REVOKE
		CHECKPOINT 			HAVING 				RIGHT
		CLOSE 				HOLDLOCK		 	ROLLBACK
		CLUSTERED 			IDENTITY		 	ROWCOUNT
		COALESCE 			IDENTITY_INSERT 	ROWGUIDCOL
		COLLATE 			IDENTITYCOL		 	RULE
		COLUMN 				IF 					SAVE
		COMMIT 				IN 					SCHEMA
		COMPUTE 			INDEX			 	SELECT
		CONSTRAINT 			INNER 				SESSION_USER
		CONTAINS 			INSERT 				SET
		CONTAINSTABLE 		INTERSECT		 	SETUSER
		CONTINUE 			INTO 				SHUTDOWN
		CONVERT 			IS 					SOME
		CREATE 				JOIN			 	STATISTICS
		CROSS 				KEY					SYSTEM_USER
		CURRENT 			KILL			 	TABLE
		CURRENT_DATE 		LEFT			 	TEXTSIZE
		CURRENT_TIME 		LIKE			 	THEN
		CURRENT_TIMESTAMP 	LINENO				TO
		CURRENT_USER 		LOAD 				TOP
		CURSOR 				NATIONAL 			TRAN
		DATABASE 			NOCHECK 			TRANSACTION
		DBCC 				NONCLUSTERED 		TRIGGER
		DEALLOCATE 			NOT 				TRUNCATE
		DECLARE 			NULL 				TSEQUAL
		DEFAULT 			NULLIF 				UNION
		DELETE 				OF 					UNIQUE
		DENY 				OFF 				UPDATE
		DESC 				OFFSETS				UPDATETEXT
		DISK 				ON 					USE
		DISTINCT	 		OPEN 				USER
		DISTRIBUTED 		OPENDATASOURCE 		VALUES
		DOUBLE 				OPENQUERY 			VARYING
		DROP 				OPENROWSET 			VIEW
		DUMMY 				OPENXML 			WAITFOR
		DUMP 				OPTION 				WHEN
		ELSE 				OR 					WHERE
		END 				ORDER 				WHILE
		ERRLVL 				OUTER				WITH
		ESCAPE 				OVER				WRITETEXT
	</xsl:variable>
	<xsl:variable name="sqlkeywords" select="concat(' ', normalize-space($sqlkeywords-multiline), ' ')"/>

	<xsl:template match="adl:application">
		<hibernate-mapping>
			<xsl:attribute name="namespace">
				<xsl:value-of select="$namespace"/>
			</xsl:attribute>
			<xsl:attribute name="assembly">
				<xsl:value-of select="$assembly"/>
			</xsl:attribute>
			<xsl:comment>
    ***************************************************************************
    *
    *     <xsl:value-of select="$product-version"/>
    *	  <xsl:value-of select="@name"/>.auto.hbm.xml
    *
    *	  ©2007 Cygnet Solutions Ltd
    *
    *	  THIS FILE IS AUTOMATICALLY GENERATED AND SHOULD NOT
    *	  BE MANUALLY EDITED.
    *
    *	  Generated using adl2hibernate.xslt revision <xsl:value-of select="substring('$Revision: 1.11 $', 12)"/>
    *
    ***************************************************************************
			</xsl:comment>

			<xsl:apply-templates select="adl:entity"/>
		</hibernate-mapping>

	</xsl:template>

	<xsl:template match="adl:entity[@foreign='true']"/>

	<xsl:template match="adl:entity">
		<xsl:apply-templates select="adl:documentation"/>
		<xsl:variable name="prefix">
			<xsl:choose>
				<xsl:when test="string-length( $database) &gt; 0">
					<xsl:value-of select="concat( $database, '.dbo.')"/>
				</xsl:when>
				<xsl:otherwise/>
			</xsl:choose>
		</xsl:variable>
		<class>
			<xsl:attribute name="name">
				<xsl:value-of select="@name"/>
			</xsl:attribute>
			<xsl:attribute name="table">
				<xsl:choose>
					<xsl:when test="@table">
						<xsl:value-of select="concat( $prefix, '[', @table, ']')"/>
					</xsl:when>
					<xsl:otherwise>
						<xsl:value-of select="concat( $prefix, '[', @name, ']')"/>
					</xsl:otherwise>
				</xsl:choose>
			</xsl:attribute>
			<xsl:apply-templates select="adl:key"/>
			<xsl:apply-templates select="adl:property"/>
		</class>
	</xsl:template>

	<xsl:template match="adl:key">
		<xsl:choose>
			<xsl:when test="count( adl:property) = 0">
			</xsl:when>
			<xsl:when test="count( adl:property) = 1">
				<id>
					<xsl:attribute name="name">
						<xsl:value-of select="adl:property[position()=1]/@name"/>
					</xsl:attribute>
					<xsl:attribute name="column">
						<xsl:call-template name="property-column-name">
							<xsl:with-param name="property" select="adl:property[position()=1]"/>
						</xsl:call-template>
					</xsl:attribute>
					<xsl:attribute name="type">
						<xsl:call-template name="csharp-base-type">
							<xsl:with-param name="property" select="adl:property[position()=1]"/>
						</xsl:call-template>
					</xsl:attribute>
					<xsl:choose>
						<xsl:when test="adl:property[position()=1]/adl:generator">
							<xsl:attribute name="name">
								<xsl:value-of select="adl:property[position()=1]/@name"/>
							</xsl:attribute>
							<xsl:apply-templates select="adl:property[position()=1]/adl:generator"/>
						</xsl:when>
						<xsl:when test="adl:property[position()=1 and @type='entity']">
							<xsl:attribute name="name">
								<xsl:value-of select="concat( adl:property[position()=1]/@name, '_Value')"/>
							</xsl:attribute>
							<xsl:variable name="entityname" select="adl:property[position()=1]/@entity"/>
							<xsl:variable name="farkey">
								<xsl:choose>
									<xsl:when test="adl:property[position()=1]/@farkey">
										<xsl:value-of select="adl:property[position()=1]/@farkey"/>
									</xsl:when>
									<xsl:when test="//adl:entity[@name=$entityname]/adl:key/adl:property">
										<xsl:value-of select="//adl:entity[@name=$entityname]/adl:key/adl:property[position()=1]/@name"/>
									</xsl:when>
									<xsl:otherwise>
										<xsl:value-of select="'[unkown?]'"/>
									</xsl:otherwise>
								</xsl:choose>
							</xsl:variable>
							<generator class="foreign">
								<param name="property">
									<xsl:value-of select="$farkey"/>
								</param>
							</generator>
						</xsl:when>
						<xsl:otherwise>
							<xsl:comment>TODO: remember you need to deal with this in manually maintained code</xsl:comment>
							<generator class="assigned"/>
						</xsl:otherwise>
					</xsl:choose>
				</id>
				<xsl:if test="adl:property[position()=1 and @type='entity']">
					<xsl:apply-templates select="adl:property[position()=1]"/>
				</xsl:if>
			</xsl:when>
			<xsl:otherwise>
				<composite-id>
					<!-- xsl:attribute name="name">
            <xsl:choose>
              <xsl:when test="$abstract-key-name-convention='Name'">
                <xsl:value-of select="ancestor::adl:entity/@name"/>
              </xsl:when>
              <xsl:when test="$abstract-key-name-convention = 'NameId'">
                <xsl:value-of select="concat( ancestor::adl:entity/@name, 'Id')"/>
              </xsl:when>
              <xsl:when test="$abstract-key-name-convention = 'Name_Id'">
                <xsl:value-of select="concat( ancestor::adl:entity/@name, '_Id')"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="'Id'"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:attribute -->
					<xsl:for-each select="adl:property[not(@type='entity')]">
						<key-property>
							<xsl:attribute name="name">
								<xsl:value-of select="@name"/>
							</xsl:attribute>
							<xsl:attribute name="column">
								<xsl:call-template name="property-column-name">
									<xsl:with-param name="property" select="."/>
								</xsl:call-template>
							</xsl:attribute>
							<xsl:attribute name="type">
								<xsl:call-template name="csharp-type">
									<xsl:with-param name="property" select="."/>
								</xsl:call-template>
							</xsl:attribute>
							<xsl:apply-templates select="adl:documentation"/>
						</key-property>
					</xsl:for-each>
					<xsl:for-each select="adl:property[@type='entity']">
						<key-many-to-one>
							<xsl:attribute name="name">
								<xsl:value-of select="@name"/>
							</xsl:attribute>
							<xsl:attribute name="column">
								<xsl:call-template name="property-column-name">
									<xsl:with-param name="property" select="."/>
								</xsl:call-template>
							</xsl:attribute>
							<xsl:attribute name="class">
								<xsl:value-of select="@entity"/>
							</xsl:attribute>
							<xsl:choose>
								<xsl:when test="@cascade='manual'"/>
								<xsl:when test="@cascade">
									<xsl:attribute name="cascade">
										<xsl:value-of select="@cascade"/>
									</xsl:attribute>
								</xsl:when>
							</xsl:choose>
							<xsl:apply-templates select="adl:documentation"/>
						</key-many-to-one>
					</xsl:for-each>
				</composite-id>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="adl:generator">
		<generator>
			<xsl:attribute name="class">
				<xsl:choose>
					<xsl:when test="@action='manual'">
						<xsl:value-of select="@class"/>
					</xsl:when>
					<xsl:otherwise>
						<xsl:value-of select="@action"/>
					</xsl:otherwise>
				</xsl:choose>
			</xsl:attribute>
		</generator>
	</xsl:template>

	<xsl:template match="adl:property[@concrete='false']">
		<!-- properties which are not concrete are by definition not 
    stored in the database -->
	</xsl:template>

	<xsl:template match="adl:property[@type='message']">
		<xsl:choose>
			<xsl:when test="@concrete='false'"/>
			<xsl:otherwise>
				<many-to-one class="Message">
					<xsl:attribute name="name">
						<xsl:value-of select="@name"/>
					</xsl:attribute>
					<xsl:if test="@column">
						<xsl:attribute name="column">
							<xsl:call-template name="property-column-name">
								<xsl:with-param name="property" select="."/>
							</xsl:call-template>
						</xsl:attribute>
					</xsl:if>
				</many-to-one>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="adl:property[@type='entity']">
		<!-- a property of type entity translates to a Hibernate many-to-one,
			unless it's part of the key, in which case it translates as one-to-one.
			TODO: Check this logic! -->
		<!-- xsl:choose>
      <xsl:when test="parent::adl:key">
        <one-to-one>
          <xsl:attribute name="name">
            <xsl:value-of select="@name"/>
          </xsl:attribute>
          <xsl:attribute name="class">
            <xsl:value-of select="@entity"/>
          </xsl:attribute>
          <xsl:if test="@farkey">
            <xsl:attribute name="property-ref">
              <xsl:value-of select="@farkey"/>
            </xsl:attribute>
          </xsl:if>
          <xsl:choose>
            <xsl:when test="@cascade='manual'"/>
            <xsl:when test="@cascade">
              <xsl:attribute name="cascade">
                <xsl:value-of select="@cascade"/>
              </xsl:attribute>
            </xsl:when>
          </xsl:choose>
          <xsl:apply-templates select="adl:documentation"/>
        </one-to-one>
      </xsl:when>
      <xsl:otherwise -->
		<xsl:choose>
			<xsl:when test="@concrete='false'"/>
			<xsl:otherwise>
				<many-to-one>
					<xsl:attribute name="name">
						<xsl:value-of select="@name"/>
					</xsl:attribute>
					<xsl:attribute name="class">
						<xsl:value-of select="@entity"/>
					</xsl:attribute>
					<xsl:attribute name="column">
						<xsl:call-template name="property-column-name">
							<xsl:with-param name="property" select="."/>
						</xsl:call-template>
					</xsl:attribute>
					<xsl:if test="@farkey">
						<xsl:attribute name="property-ref">
							<xsl:value-of select="@farkey"/>
						</xsl:attribute>
					</xsl:if>
					<xsl:choose>
						<xsl:when test="@cascade='manual'"/>
						<xsl:when test="@cascade">
							<xsl:attribute name="cascade">
								<xsl:value-of select="@cascade"/>
							</xsl:attribute>
						</xsl:when>
					</xsl:choose>
					<xsl:apply-templates select="adl:documentation"/>
				</many-to-one>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="adl:property[@type='list']">
		<xsl:choose>
			<xsl:when test="@concrete='false'"/>
			<xsl:otherwise>
				<xsl:variable name="farent" select="@entity"/>
				<xsl:variable name="nearent" select="ancestor::adl:entity/@name"/>
				<xsl:variable name="farkey">
					<xsl:choose>
						<xsl:when test="@farkey">
							<xsl:value-of select="@farkey"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:value-of select="ancestor::adl:entity/@name"/>
						</xsl:otherwise>
					</xsl:choose>
				</xsl:variable>
				<set>
					<xsl:attribute name="name">
						<xsl:value-of select="@name"/>
					</xsl:attribute>
					<xsl:attribute name="inverse">
						<!-- true if the other end of the link is described in the ADL (which it normally will be) -->
						<xsl:choose>
							<xsl:when test="//adl:entity[@name=$farent]/adl:property[@name=$farkey and @entity=$nearent]">true</xsl:when>
							<xsl:otherwise>false</xsl:otherwise>
						</xsl:choose>
					</xsl:attribute>
					<xsl:apply-templates select="adl:documentation"/>
					<!-- careful with reformatting here: 
					'The element cannot contain white space. Content model is empty.' -->
					<key>
						<xsl:attribute name="column">
							<!-- this is the name of the farside foreign key field which points to me -->
							<xsl:value-of select="$farkey"/>
						</xsl:attribute>
					</key>
					<one-to-many>
						<xsl:attribute name="class">
							<xsl:value-of select="@entity"/>
						</xsl:attribute>
					</one-to-many>
					<xsl:choose>
						<xsl:when test="@cascade='manual'"/>
						<xsl:when test="@cascade">
							<xsl:attribute name="cascade">
								<xsl:value-of select="@cascade"/>
							</xsl:attribute>
						</xsl:when>
					</xsl:choose>
				</set>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="adl:property[@type='link']">
		<!-- a property of type 'link' maps on to a Hibernate set -->
		<xsl:choose>
			<xsl:when test="@concrete='false'"/>
			<xsl:otherwise>
				<xsl:variable name="comparison">
					<xsl:call-template name="stringcompare">
						<xsl:with-param name="node1" select="../@name"/>
						<xsl:with-param name="node2" select="@entity"/>
					</xsl:call-template>
				</xsl:variable>
				<xsl:variable name="tablename">
					<xsl:choose>
						<xsl:when test="$comparison =-1">
							<xsl:value-of select="concat( $dbprefix, 'ln_', ../@name, '_', @entity)"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:value-of select="concat( $dbprefix, 'ln_', @entity, '_', ../@name)"/>
						</xsl:otherwise>
					</xsl:choose>
				</xsl:variable>

				<set>
					<xsl:apply-templates select="adl:documentation"/>
					<xsl:attribute name="name">
						<xsl:value-of select="@name"/>
					</xsl:attribute>
					<xsl:attribute name="table">
						<xsl:value-of select="$tablename"/>
					</xsl:attribute>
					<key>
						<xsl:attribute name="column">
							<xsl:value-of select="concat( ../@name, 'Id')"/>
						</xsl:attribute>
					</key>
					<many-to-many>
						<xsl:attribute name="column">
							<xsl:choose>
								<xsl:when test="../@name = @entity">
									<xsl:value-of select="concat( @entity, '_1Id')"/>
								</xsl:when>
								<xsl:otherwise>
									<xsl:value-of select="concat( @entity, 'Id')"/>
								</xsl:otherwise>
							</xsl:choose>
						</xsl:attribute>
						<xsl:attribute name="class">
							<xsl:value-of select="@entity"/>
						</xsl:attribute>
					</many-to-many>
				</set>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="adl:property">
		<!-- tricky, this, because we're translating between ADL properties and 
    Hibernate properties, which are (slightly) different. There's potential 
    for confusion -->
		<property>
			<xsl:attribute name="name">
				<xsl:value-of select="@name"/>
			</xsl:attribute>
			<xsl:attribute name="type">
				<xsl:call-template name="csharp-type">
					<xsl:with-param name="property" select="."/>
				</xsl:call-template>
			</xsl:attribute>
			<xsl:attribute name="column">
				<xsl:call-template name="property-column-name">
					<xsl:with-param name="property" select="."/>
				</xsl:call-template>
			</xsl:attribute>
			<xsl:apply-templates select="adl:documentation"/>
		</property>
	</xsl:template>

	<xsl:template match="adl:documentation">
		<xsl:comment>
			<xsl:apply-templates/>
		</xsl:comment>
	</xsl:template>

	<!-- consistent, repeatable way of getting the column name for a given property -->
	<xsl:template name="property-column-name">
		<!-- a property element -->
		<xsl:param name="property"/>
		<xsl:variable name="unescaped">
			<xsl:choose>
				<xsl:when test="$property/@column">
					<xsl:value-of select="$property/@column"/>
				</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="$property/@name"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>
		<xsl:choose>
			<xsl:when test="contains( $sqlkeywords, concat(' ', translate( $unescaped, $lcase, $ucase),' '))">
				<xsl:value-of select="concat( '[', $unescaped, ']')"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="$unescaped"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>


	<!-- 
    horrible, horrible hackery. Compare two strings and return 
        * 0 if they are identical, 
        * -1 if the first is earlier in the default collating sequence, 
        * 1 if the first is later. 
    In XSL 2.0 this could be done using the compare(string, string) function.
    TODO: probably should be an include file
  -->
	<xsl:template name="stringcompare">
		<xsl:param name="node1"/>
		<xsl:param name="node2"/>
		<xsl:choose>
			<xsl:when test="string($node1)=string($node2)">
				<xsl:text>0</xsl:text>
			</xsl:when>
			<xsl:otherwise>
				<xsl:for-each select="$node1 | $node2">
					<xsl:sort select="."/>
					<xsl:if test="position()=1">
						<xsl:choose>
							<xsl:when test="string(.) = string($node1)">
								<xsl:text>-1</xsl:text>
							</xsl:when>
							<xsl:otherwise>
								<xsl:text>1</xsl:text>
							</xsl:otherwise>
						</xsl:choose>
					</xsl:if>
				</xsl:for-each>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

</xsl:stylesheet>