<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!--
      C1873 SRU Hospitality
      adl2mssql.xsl
      
      (c) 2007 Cygnet Solutions Ltd
      
      Convert ADL to MS-SQL
      
      $Author: af $
      $Revision: 1.1 $
  -->
    
    <xsl:output indent="no" encoding="utf-8" method="text"/>
    
    <xsl:template match="application"> 
        -------------------------------------------------------------------------------------------------
        --
        --    Database for application <xsl:value-of select="@name"/> version <xsl:value-of select="@version"/>
        --    Generated for MS-SQL 2000+ using adl2mssql.xsl $Revision: 1.1 $
        --
        --    Code generator (c) 2007 Cygnet Solutions Ltd
        --
        -------------------------------------------------------------------------------------------------

        -------------------------------------------------------------------------------------------------
        --    authentication roles
        -------------------------------------------------------------------------------------------------
        <xsl:apply-templates select="group"/>

        -------------------------------------------------------------------------------------------------
        --    tables, views and permissions
        -------------------------------------------------------------------------------------------------
        <xsl:apply-templates select="entity" mode="table"/>

        <xsl:apply-templates select="entity" mode="view"/>

        -------------------------------------------------------------------------------------------------
        --    referential integrity constraints
        -------------------------------------------------------------------------------------------------
            <xsl:for-each select="entity">
              <xsl:variable name="nearside" select="@name"/>
              <xsl:for-each select="property[@type='entity']">
                <xsl:call-template name="referentialintegrity">
                    <xsl:with-param name="nearside" select="$nearside"/>
                </xsl:call-template>
                
            </xsl:for-each>
        </xsl:for-each>
      
        -------------------------------------------------------------------------------------------------
        --    end of file
        -------------------------------------------------------------------------------------------------
    </xsl:template>
    
    <xsl:template match="group">
        execute sp_addrole @rolename = '<xsl:value-of select="@name"/>' 
        
        GO
    </xsl:template>
    
    
    <xsl:template name="referentialintegrity">
        <xsl:param name="nearside"/>
        <!-- set up referential integrity constraints for primary tables -->
        ALTER TABLE "<xsl:value-of select="$nearside"/>"
            ADD FOREIGN KEY ( "<xsl:value-of select="@name"/>") 
            REFERENCES "<xsl:value-of select="@entity"/>" ON DELETE NO ACTION
            
        GO
    </xsl:template>


    <xsl:template match="entity" mode="table">
        <xsl:variable name="table" select="@name"/>

        -------------------------------------------------------------------------------------------------
        --    primary table <xsl:value-of select="@name"/>
        -------------------------------------------------------------------------------------------------
        CREATE TABLE  "<xsl:value-of select="@name"/>"
        (
          <xsl:apply-templates select="property[@type!='link']"/>
          <xsl:value-of select="@name"/>Id INT IDENTITY( 1, 1) PRIMARY KEY
        )
        
        GO

        ----  permissions  ------------------------------------------------------------------------------
        <xsl:for-each select="permission">
          <xsl:call-template name="permission">
            <xsl:with-param name="table" select="$table"/>
          </xsl:call-template>
        </xsl:for-each>

      </xsl:template>
  
      <xsl:template match="entity" mode="view">
        <xsl:variable name="table" select="@name"/>
        -------------------------------------------------------------------------------------------------
        --    convenience view VW_DL_<xsl:value-of select="@name"/> for default list
        -------------------------------------------------------------------------------------------------
        
        CREATE VIEW "VW_DL_<xsl:value-of select="@name"/>" AS
        SELECT "<xsl:value-of select="@name"/>"."<xsl:value-of select="@name"/>Id",
        <xsl:for-each select="property[@type!='link']">
            <xsl:choose>
               <xsl:when test="@type='entity'">
                   <xsl:call-template name="distinctfield">
                       <xsl:with-param name="table" select="@entity"/>
                       <xsl:with-param name="alias" select="@name"/>
                   </xsl:call-template> AS <xsl:value-of select="@name"/></xsl:when>
                <xsl:otherwise>"<xsl:value-of select="$table"/>"."<xsl:value-of select="@name"/>"</xsl:otherwise>
            </xsl:choose><xsl:choose>
                <xsl:when test="position() = last()"></xsl:when>
                <xsl:otherwise>,
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>
        FROM  "<xsl:value-of select="@name"/>" <xsl:for-each 
          select="property[@type='entity']">, "<xsl:value-of select="@entity"/>" AS "<xsl:value-of select="@name"/>"</xsl:for-each>
        <xsl:text>
        </xsl:text>
        <xsl:for-each select="property[@type='entity']">
            <xsl:choose>
            <xsl:when test="position() = 1">WHERE </xsl:when>
            <xsl:otherwise>AND   </xsl:otherwise>
            </xsl:choose>"<xsl:value-of select="$table"/>"."<xsl:value-of 
                select="@name"/>" = "<xsl:value-of select="@name"/>"."<xsl:value-of select="@entity"/>Id"
        </xsl:for-each>
        
        GO

        ----  permissions  ------------------------------------------------------------------------------
        <xsl:for-each select="permission">
            <xsl:call-template name="viewpermission">
                <xsl:with-param name="table" select="$table"/>
            </xsl:call-template>
        </xsl:for-each>
        
        <!-- link tables -->
        <xsl:for-each select="property[@type='link']">
            <xsl:call-template name="linktable">
                <xsl:with-param name="nearside" select="$table"/>
            </xsl:call-template>
        </xsl:for-each>
        
    </xsl:template>
     
    <xsl:template name="distinctfield">
        <xsl:param name="table"/>
        <xsl:param name="alias"/>
        <!-- 
            print the names of the distinguishing fields in this table,
            concatenating into a single string. 
        -->
        <xsl:for-each select="/application/entity[@name=$table]">
            <xsl:for-each select="property[@distinct='user' or @distinct='all']">
                <xsl:choose>
                    <xsl:when test="@type='entity'">
                        <xsl:call-template name="distinctfield">
                            <xsl:with-param name="table" select="@entity"/>
                            <xsl:with-param name="alias" select="concat( $alias, '_', @name)"></xsl:with-param>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        "<xsl:value-of select="$alias"/>"."<xsl:value-of 
                        select="@name"/>"<xsl:if test="position() != last()"> + ' ' + </xsl:if>
                    </xsl:otherwise>
                </xsl:choose>
                
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
     
    <xsl:template name="permission">
        <xsl:param name="table"/>
        <!-- decode the permissions for a table -->
        <xsl:choose>
            <xsl:when test="@permission='read'">GRANT SELECT ON "<xsl:value-of 
                select="$table"/>" TO <xsl:value-of select="@group"/> 
              
            GO</xsl:when>
            <xsl:when test="@permission='insert'">GRANT INSERT ON "<xsl:value-of 
                select="$table"/>" TO <xsl:value-of select="@group"/> 
              
            GO</xsl:when>
            <xsl:when test="@permission='noedit'">GRANT SELECT, INSERT ON "<xsl:value-of 
                select="$table"/>" TO <xsl:value-of select="@group"/> 
              
            GO</xsl:when>
            <xsl:when test="@permission='edit'">GRANT SELECT, INSERT, UPDATE ON "<xsl:value-of 
                select="$table"/>" TO <xsl:value-of select="@group"/> 
              
            GO</xsl:when>
            <xsl:when test="@permission='all'">GRANT SELECT, INSERT, UPDATE, DELETE ON "<xsl:value-of 
                select="$table"/>" TO <xsl:value-of select="@group"/> 
              
            GO</xsl:when>
            <xsl:otherwise>REVOKE ALL ON "<xsl:value-of 
                select="$table"/>" FROM <xsl:value-of select="@group"/> 
              
            GO</xsl:otherwise>
        </xsl:choose>
        <xsl:text>
            
        </xsl:text>
    </xsl:template>
     
    <xsl:template name="viewpermission">
        <xsl:param name="table"/>
        <!-- decode the permissions for a convenience view -->
        <xsl:choose>
            <xsl:when test="@permission='none'">REVOKE ALL ON "VW_DL_<xsl:value-of 
                select="$table"/>" FROM <xsl:value-of select="@group"/> 
              
                GO</xsl:when>
            <xsl:when test="@permission='insert'">REVOKE ALL ON "VW_DL_<xsl:value-of 
                select="$table"/>" FROM <xsl:value-of select="@group"/> 
              
                GO</xsl:when>
            <xsl:otherwise>GRANT SELECT ON "VW_DL_<xsl:value-of 
                select="$table"/>" TO <xsl:value-of select="@group"/> 
              
                GO</xsl:otherwise>
        </xsl:choose>
        <xsl:text>
            
        </xsl:text>
    </xsl:template>
    
    
    <xsl:template name="linktable">
        <xsl:param name="nearside"/>
      <!-- This is tricky. For any many-to-many relationship between two 
      entities, we only want to create one link table, even if (as should be) 
      a property of type 'link' has been declared at both ends -->
      <xsl:variable name="farside">
        <xsl:choose>
          <xsl:when test="@entity = $nearside">
            <xsl:value-of select="concat( @entity, '_1')"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="@entity"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:variable name="comparison">
        <xsl:call-template name="stringcompare">
          <xsl:with-param name="node1" select="$nearside"/>
          <xsl:with-param name="node2" select="@entity"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="farentity" select="/application/entity[@name=$farside]"/>
      
      -- Problems with responsibility for generating link tables:
      -- @entity = <xsl:value-of select="@entity"/>
      -- $nearside = <xsl:value-of select="$nearside"/>
      -- $farside = <xsl:value-of select="$farside"/>
      -- $farentity = <xsl:value-of select="count( $farentity/property)"/>
      -- farlink = <xsl:value-of select="$farentity/property[@type='link' and @entity=$nearside]/@name"/>
      -- comparison = '<xsl:value-of select="$comparison"/>'

      <xsl:variable name="myresponsibility">
        <xsl:choose>
          <!-- if we could use the compare( string, string) function this would be a lot simpler, but 
          unfortunately that's in XSL 2.0, and neither NAnt nor Visual Studio can manage that -->
          <!-- if the link is back to me, then obviously I'm responsible -->
          <xsl:when test="$comparison = 0">true</xsl:when>
          <!-- generally, the entity whose name is later in the default collating sequence
          shall not be responsible. -->
          <xsl:when test="$comparison = -1">true</xsl:when>
            <!-- However if the one that is earlier doesn't have a 'link' 
          property for this join, however, then later end will have to do it -->
          <xsl:when test="$comparison = 1">
            <xsl:choose>
              <!-- the far side is doing it... -->
              <xsl:when test="$farentity/property[@type='link' and @entity=$nearside]">false</xsl:when>
              <xsl:otherwise>true</xsl:otherwise>
            </xsl:choose>
          </xsl:when>
          <xsl:otherwise>false</xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:variable name="tablename">
        <xsl:choose>
          <xsl:when test="$comparison =-1">
            <xsl:value-of select="concat( 'ln_', $nearside, '_', @entity)"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="concat( 'ln_', @entity, '_', $nearside)"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      -- Responsibility = '<xsl:value-of select="$myresponsibility"/>'
      <xsl:choose>
        <xsl:when test="$myresponsibility='true'">
          <!-- create a linking table -->

          -------------------------------------------------------------------------------------------------
          --    link table joining <xsl:value-of select="$nearside"/> with <xsl:value-of select="@entity"/>
          -------------------------------------------------------------------------------------------------
          CREATE TABLE "<xsl:value-of select="$tablename"/>"
          (
            "<xsl:value-of select="$nearside"/>Id" INT NOT NULL,
            "<xsl:value-of select="$farside"/>Id" INT NOT NULL,
          )

          GO
          <xsl:text>
            
          </xsl:text>
          ----  permissions  ------------------------------------------------------------------------------
          <xsl:for-each select="../permission">
            <xsl:call-template name="permission">
              <xsl:with-param name="table" select="$tablename"/>
            </xsl:call-template>
          </xsl:for-each>
          <xsl:text>
            
          </xsl:text>
          ----  referential integrity  --------------------------------------------------------------------
          <xsl:choose>
            <xsl:when test="$nearside=@entity">
          ALTER TABLE "<xsl:value-of select="$tablename"/>"
          ADD FOREIGN KEY ( "<xsl:value-of select="$nearside"/>Id")
          REFERENCES "<xsl:value-of select="$nearside"/>" ON DELETE NO ACTION

          GO

          ALTER TABLE "<xsl:value-of select="$tablename"/>"
          ADD FOREIGN KEY ( "<xsl:value-of select="$farside"/>Id")
          REFERENCES "<xsl:value-of select="$nearside"/>" ON DELETE NO ACTION

          GO

            </xsl:when>
            <xsl:otherwise>
          ALTER TABLE "<xsl:value-of select="$tablename"/>"
          ADD FOREIGN KEY ( "<xsl:value-of select="$nearside"/>Id")
          REFERENCES "<xsl:value-of select="$nearside"/>" ON DELETE CASCADE

          GO

          ALTER TABLE "<xsl:value-of select="$tablename"/>"
          ADD FOREIGN KEY ( "<xsl:value-of select="@entity"/>Id")
          REFERENCES "<xsl:value-of select="@entity"/>" ON DELETE CASCADE

          GO


            </xsl:otherwise>
          </xsl:choose>


      </xsl:when>
        <xsl:otherwise>
          -- Suppressing generation of <xsl:value-of select="$tablename"/>, as it is not my responsibility
        </xsl:otherwise>
      </xsl:choose>
      <xsl:if test="myresponsibility='true'">
      </xsl:if>
    </xsl:template>

  <xsl:template match="property[@type='list']">
          -- Suppressing output of property <xsl:value-of select="@name"/>, 
          -- as it is the 'one' end of a one-to-many relationship
  </xsl:template>
    
    <xsl:template match="property[@type='entity']">
            "<xsl:value-of select="@name"/>" INT<xsl:if 
            test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if 
                test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
    </xsl:template>

    <xsl:template match="property[@type='defined']">
        <xsl:variable name="name"><xsl:value-of select="@definition"/></xsl:variable>
        <xsl:variable name="definitiontype"><xsl:value-of select="/application/definition[@name=$name]/@type"/></xsl:variable>
            "<xsl:value-of select="@name"/>"<xsl:text> </xsl:text><xsl:choose>
            <xsl:when test="$definitiontype='string'">VARCHAR( <xsl:value-of 
                select="/application/definition[@name=$name]/@size"/>)</xsl:when>
            <xsl:when test="$definitiontype='integer'">INT</xsl:when>
            <xsl:when test="$definitiontype='real'">DOUBLE PRECISION</xsl:when>
            <xsl:otherwise><xsl:value-of select="$definitiontype"/></xsl:otherwise>
        </xsl:choose><xsl:if 
        test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if 
            test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
    </xsl:template>


    <xsl:template match="property[@type='boolean']">
            -- SQL Server doesn't have proper booleans!
            "<xsl:value-of select="@name"/>" BIT<xsl:choose>
              <xsl:when test="@default='true'"> DEFAULT 1</xsl:when>
              <xsl:when test="@default='false'"> DEFAULT 0</xsl:when>
            </xsl:choose><xsl:if test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
    </xsl:template>


    <xsl:template match="property[@type='string']">
            "<xsl:value-of select="@name"/>" VARCHAR( <xsl:value-of select="@size"/>)<xsl:if 
                test="string(@default)"> DEFAULT '<xsl:value-of select="@default"/>'</xsl:if><xsl:if 
                    test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
    </xsl:template>

    <xsl:template match="property[@type='date' or @type = 'time']">
            "<xsl:value-of select="@name"/>" DATETIME<xsl:if
                test="string(@default)"> DEFAULT <xsl:value-of select="@default"/>
              </xsl:if><xsl:if
                test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
    </xsl:template>


  <xsl:template match="property[@type='integer']">
            "<xsl:value-of select="@name"/>" INT<xsl:if 
                test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if 
                  test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
  </xsl:template>
    
  <xsl:template match="property[@type='real']">
            "<xsl:value-of select="@name"/>" DOUBLE PRECISION<xsl:if 
                test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if 
                    test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
  </xsl:template>
    
  <xsl:template match="property">
            "<xsl:value-of select="@name"/>" <xsl:text> </xsl:text><xsl:value-of select="@type"/><xsl:if 
                test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if 
                    test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
            </xsl:text>
  </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. -->
  <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>