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

  <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:variable name="namespace">SRU.Hospitality.Entities</xsl:variable>
  <xsl:variable name="assembly">SRU.Hospitality.DataModel</xsl:variable>

  <xsl:template match="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>
          ***************************************************************************
          *
          *	C1873: C1873-SUS-Hospitality.auto.hbm.xml
          *
          *	©2007 Cygnet Solutions Ltd
          *
          *	THIS FILE IS AUTOMATICALLY GENERATED AND SHOULD NOT
          *	BE MANUALLY EDITED. 
          *
          *	Generated using adl2hibernate-mapping.xsl revision <xsl:value-of select="substring('$Revision: 1.1 $', 12)"/>
          *
          ***************************************************************************
        </xsl:comment>
        <xsl:apply-templates select="entity"/>
      </hibernate-mapping>

  </xsl:template>

  <xsl:template match="entity">
    <class>
      <xsl:attribute name="name">
        <xsl:value-of select="@name"/>
      </xsl:attribute>
      <xsl:attribute name="table">
        <xsl:value-of select="@name"/>
      </xsl:attribute>
      <id type="Int32">
        <!-- ADL does not encode the primary key explicitly; instead it is 
        implicit and its name is always the name of the entity followed by 
        'Id' -->
        <xsl:attribute name="name">
          <xsl:value-of select="normalize-space( concat( @name, 'Id'))"/>
        </xsl:attribute>
        <generator class="native"/>
      </id>
      <xsl:apply-templates select="property"/>
    </class>
  </xsl:template>

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

  <xsl:template match="property[@type='entity']">
    <!-- a property of type entity translates to a Hibernate many-to-one -->
    <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>
    </many-to-one>
  </xsl:template>

  <xsl:template match="property[@type='list']">
    <xsl:variable name="farent" select="@entity"/>
    <xsl:variable name="nearent" select="ancestor::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::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="//entity[@name=$farent]/property[@name=$farkey and @entity=$nearent]">true</xsl:when>
          <xsl:otherwise>false</xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
      <!-- 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>
    </set>
  </xsl:template>

  <xsl:template match="property[@type='link']">
    <!-- a property of type 'link' maps on to a Hibernate set -->
    <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( 'ln_', ../@name, '_', @entity)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="concat( 'ln_', @entity, '_', ../@name)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <set>
      <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:template>
  
  <xsl:template match="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:variable name="type">
          <xsl:choose>
            <xsl:when test="@type = 'defined'">
                <xsl:variable name="definition">
                  <xsl:value-of select="@definition"/>
                </xsl:variable>
                <xsl:value-of select="/application/definition[@name=$definition]/@type"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="@type"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <xsl:choose>
          <xsl:when test="$type = 'date'">DateTime</xsl:when>
          <xsl:when test="$type = 'time'">DateTime</xsl:when>
          <xsl:when test="$type = 'string'">String</xsl:when>
          <xsl:when test="$type = 'text'">String</xsl:when>
          <xsl:when test="$type = 'boolean'">Boolean</xsl:when>
          <xsl:when test="$type = 'timestamp'">TimeStamp</xsl:when>
          <xsl:when test="$type = 'integer'">Int32</xsl:when>
          <xsl:when test="$type = 'real'">Double</xsl:when>
          <xsl:when test="$type = 'money'">Decimal</xsl:when>
          <xsl:otherwise>[unknown?]</xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
    </property>
  </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>