<xsl:stylesheet xmlns:xd="http://www.pnp-software.com/XSLTdoc" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns="http://www.tei-c.org/ns/1.0" version="1.0" exclude-result-prefixes="xd tei">
  
  <xd:doc type="stylesheet">
    
<xd:short>Expands a list of MSDs according to the morphosyntactic specifications.</xd:short>
    
<xd:author>Tomaž Erjavec, <A href="http://nl.ijs.si/et/">http://nl.ijs.si/et/</A></xd:author>
    
<xd:date>2008-04-15</xd:date>
    
<xd:detail> With TEI P5 MULTEXT-East type morphosyntactic specification as input XML converts a
      list of MSDs to various formats and localisations. If xsl:output is set to text, the output is
      a tab-separated file, if to xml, the output is a TEI table. 
</xd:detail>
    
<xd:copyright> This library is free software; you can redistribute it and/or modify it under the
      terms of the GNU Lesser General Public License as published by the Free Software Foundation;
      either version 2.1 of the License, or (at your option) any later version. This library is
      distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
      implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
      General Public License for more details. You should have received a copy of the GNU Lesser
      General Public License along with this library; if not, write to the Free Software Foundation,
      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
</xd:copyright>
  
</xd:doc>


  
  <xd:doc type="string">If xsl:output/@method is xml outputs TEI table, 
    if to text a tab separated file
</xd:doc>

  
<xsl:output method="text" />

  
  <xd:doc type="string"> What to output. Modes should be given as space separated list. id = input
    MSD; msd = output MSD; collate = output collation (sort key) of MSD, e.g. 1S120200, hyphen maps
    to 00. cat = category (part-of-speech) att = attributes and category val = values attval = pairs
    attribute=value brief = decorated values, for readibility, e.g. +animate 
</xd:doc>

  
<xsl:param name="modes">verbose</xsl:param>

  
  <xd:doc type="string"> Output header row in table? NOT YET!! </xd:doc>

  
<xsl:param name="header">yes</xsl:param>

  
  <xd:doc type="string"> Which cannonical form to produce. Features can be unspecified (have as
    their value a hyphen, '-', meaning "non-applicable"), and this parameters determines if and
    which such features should be output. Valid values are: none = only features where value is
    specified. cat = all features valid for the category full = all features regardless of the
    category The effect of this parameter is mode dependent, e.g. hyphens cannot be deleted in the
    middle of the output MSD string. 
</xd:doc>

  
<xsl:param name="canonical">none</xsl:param>

  
  <xd:doc type="string">Language of input MSDs.</xd:doc>

  
<xsl:param name="lang-in">en</xsl:param>

  
  <xd:doc type="string">Language of output MSDs.</xd:doc>

  
<xsl:param name="lang-out">en</xsl:param>

  
  <xd:doc type="string">List of input MSDs, separated by spaces. If parameter is not set, the MSDs
    are taken from the specifications. NOT YET!!
</xd:doc>

  
<xsl:param name="msds" />

  
  <xd:doc type="string">Primary separator between input MSD and result.</xd:doc>

  
<xsl:variable name="primary-separator">
    
<xsl:text>        </xsl:text>
  
</xsl:variable>

  
  <xd:doc type="string">Secondary separator between features.</xd:doc>

  
<xsl:variable name="secondary-separator">
    
<xsl:text> </xsl:text>
  
</xsl:variable>

  
<!-- alas, variables not allowed in @use
  <xd:doc>PoS tables keyed to $lang-in PoS code.</xd:doc>
  <xsl:key name="cattable" match="tei:table[@n='msd.cat']" 
    use="tei:row[@role='type']/tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]]"/>
   
-->

  
  <xd:doc> Output expanded MSD list as table. </xd:doc>

  
<xsl:template match="/">
    
<xsl:if test="not(normalize-space($msds))">
      
<xsl:message terminate="yes">Need MSD list! (for now)</xsl:message>
    
</xsl:if>
    
<table n="msd-expand {$modes}">
      
<xsl:text>
</xsl:text>
      
<xsl:call-template name="expand-msds">
        
<xsl:with-param name="msds" select="concat(normalize-space($msds),' ')" />
      
</xsl:call-template>
    
</table>
  
</xsl:template>

  
  <xd:doc> Split the list of MSDs and process each. <xd:param name="msds" type="string">MSDs to
      process.
</xd:param>
  
</xd:doc>

  
<xsl:template name="expand-msds">
    
<xsl:param name="msds" />
    
<xsl:if test="normalize-space($msds)">
      
<row>
        
<xsl:call-template name="expand-msd">
          
<xsl:with-param name="msd" select="substring-before($msds,' ')" />
          
<xsl:with-param name="modes" select="concat(normalize-space($modes),' ')" />
        
</xsl:call-template>
      
</row>
      
<xsl:text>
</xsl:text>
      
<xsl:call-template name="expand-msds">
        
<xsl:with-param name="msds" select="substring-after($msds,' ')" />
      
</xsl:call-template>
    
</xsl:if>
  
</xsl:template>

  
  <xd:doc> Expand one MSD. <xd:param name="msd" type="string">MSD to process.</xd:param>
    
<xd:param name="modes" type="string">Output modes.</xd:param>
  
</xd:doc>

  
<xsl:template name="expand-msd">
    
<xsl:param name="msd" />
    
<xsl:param name="modes" />
    
<xsl:if test="normalize-space($modes)">
      
<xsl:variable name="mode" select="substring-before($modes,' ')" />
      
<cell>
        
<xsl:variable name="result">
          
<xsl:call-template name="expand-msd-mode">
            
<xsl:with-param name="msd" select="$msd" />
            
<xsl:with-param name="mode" select="$mode" />
          
</xsl:call-template>
        
</xsl:variable>
        
<xsl:choose>
          
<xsl:when test="$mode='msd' or $mode='collate'">
            
<xsl:value-of select="translate($result,$secondary-separator,'')" />
          
</xsl:when>
          
<xsl:otherwise>
            
<xsl:value-of select="normalize-space($result)" />
          
</xsl:otherwise>
        
</xsl:choose>
      
</cell>
      
<xsl:if test="normalize-space(substring-after($modes,' '))">
        
<xsl:value-of select="$primary-separator" />
        
<xsl:call-template name="expand-msd">
          
<xsl:with-param name="msd" select="$msd" />
          
<xsl:with-param name="modes" select="substring-after($modes,' ')" />
        
</xsl:call-template>
      
</xsl:if>
    
</xsl:if>
  
</xsl:template>

  
  <xd:doc> Expand one MSD for given mode <xd:param name="msd" type="string">MSD to process.</xd:param>
    
<xd:param name="mode" type="string">Output mode.</xd:param>
  
</xd:doc>

  
<xsl:template name="expand-msd-mode">
    
<xsl:param name="msd" />
    
<xsl:param name="mode" />
    
<xsl:choose>
      
<xsl:when test="$mode='id'">
        
<xsl:value-of select="$msd" />
      
</xsl:when>
      
<xsl:otherwise>
        
<xsl:variable name="result">
          
<xsl:call-template name="expand-feat-mode">
            
<xsl:with-param name="n">0</xsl:with-param>
            
<xsl:with-param name="cat" select="substring($msd,1,1)" />
            
<xsl:with-param name="codes" select="$msd" />
            
<xsl:with-param name="mode" select="$mode" />
          
</xsl:call-template>
        
</xsl:variable>
        
<xsl:choose>
          
<xsl:when test="$canonical='full'">
            
<xsl:variable name="atts">
              
<xsl:call-template name="all-atts" />
            
</xsl:variable>
            
<xsl:variable name="msd-atts">
              
<xsl:call-template name="expand-feat-mode">
                
<xsl:with-param name="n">0</xsl:with-param>
                
<xsl:with-param name="cat" select="substring($msd,1,1)" />
                
<xsl:with-param name="codes" select="$msd" />
                
<xsl:with-param name="mode">att</xsl:with-param>
              
</xsl:call-template>
            
</xsl:variable>
            
<xsl:value-of select="substring-before($result,$secondary-separator)" />
            
<xsl:value-of select="$secondary-separator" />
            
<xsl:call-template name="canon">
              
<xsl:with-param name="atts" select="$atts" />
              
<xsl:with-param name="msd-atts" select="concat(substring-after(normalize-space($msd-atts),$secondary-separator),$secondary-separator)" />
              
<xsl:with-param name="result" select="concat(substring-after(normalize-space($result),$secondary-separator),$secondary-separator)" />
              
<xsl:with-param name="mode" select="$mode" />
            
</xsl:call-template>
          
</xsl:when>
          
<xsl:otherwise>
            
<xsl:value-of select="normalize-space($result)" />
          
</xsl:otherwise>
        
</xsl:choose>
      
</xsl:otherwise>
    
</xsl:choose>
  
</xsl:template>

  
<!-- full canonical -->
  
<xsl:template name="all-atts">
    
<xsl:for-each select="//tei:table[@n='msd.cat']/ tei:row[@role='attribute']">
      
<xsl:variable name="att" select="tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
      
<xsl:if test="not(parent::tei:table/preceding::tei:table[@n='msd.cat']/ tei:row[@role='attribute'] [tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out] = $att] )">
        
<xsl:value-of select="$att" />
        
<xsl:value-of select="$secondary-separator" />
      
</xsl:if>
    
</xsl:for-each>
  
</xsl:template>

  
<xsl:template name="canon">
    
<xsl:param name="atts" />
    
<xsl:param name="msd-atts" />
    
<xsl:param name="result" />
    
<xsl:param name="mode" />
    
<xsl:if test="normalize-space($atts)">
      
<xsl:variable name="att" select="substring-before($atts,$secondary-separator)" />
      
<xsl:choose>
        
<xsl:when test="contains( concat($secondary-separator,$msd-atts,$secondary-separator), concat($secondary-separator,$att,$secondary-separator))">
          
<xsl:call-template name="canon-output">
            
<xsl:with-param name="att" select="$att" />
            
<xsl:with-param name="msd-atts" select="$msd-atts" />
            
<xsl:with-param name="result" select="$result" />
          
</xsl:call-template>
        
</xsl:when>
        
<xsl:otherwise>
          
<xsl:call-template name="not-applicable">
            
<xsl:with-param name="mode" select="$mode" />
            
<xsl:with-param name="att" select="$att" />
          
</xsl:call-template>
          
<xsl:value-of select="$secondary-separator" />
        
</xsl:otherwise>
      
</xsl:choose>
      
<xsl:call-template name="canon">
        
<xsl:with-param name="atts" select="substring-after($atts,$secondary-separator)" />
        
<xsl:with-param name="msd-atts" select="$msd-atts" />
        
<xsl:with-param name="result" select="$result" />
        
<xsl:with-param name="mode" select="$mode" />
      
</xsl:call-template>
    
</xsl:if>
  
</xsl:template>

  
<xsl:template name="canon-output">
    
<xsl:param name="att" />
    
<xsl:param name="msd-atts" />
    
<xsl:param name="result" />
    
<xsl:if test="not(normalize-space($msd-atts))">
      
<xsl:text>Can't find </xsl:text>
      
<xsl:value-of select="$att" />
      
<xsl:text> attribute in canonical string for </xsl:text>
      
<xsl:value-of select="$msd-atts" />
      
<xsl:text> in </xsl:text>
      
<xsl:value-of select="$result" />
      
<xsl:message terminate="yes">
        
<xsl:text>Can't find </xsl:text>
        
<xsl:value-of select="$att" />
        
<xsl:text> attribute in canonical string for </xsl:text>
        
<xsl:value-of select="$msd-atts" />
        
<xsl:text> in </xsl:text>
        
<xsl:value-of select="$result" />
      
</xsl:message>
    
</xsl:if>
    
<xsl:variable name="msd-att" select="substring-before($msd-atts,$secondary-separator)" />
    
<xsl:choose>
      
<xsl:when test="starts-with($msd-atts,concat($att,$secondary-separator))">
        
<xsl:value-of select="substring-before($result,$secondary-separator)" />
        
<xsl:value-of select="$secondary-separator" />
      
</xsl:when>
      
<xsl:otherwise>
        
<xsl:call-template name="canon-output">
          
<xsl:with-param name="att" select="$att" />
          
<xsl:with-param name="msd-atts" select="substring-after($msd-atts,$secondary-separator)" />
          
<xsl:with-param name="result" select="substring-after($result,$secondary-separator)" />
        
</xsl:call-template>
      
</xsl:otherwise>
    
</xsl:choose>
  
</xsl:template>

  
<!-- MSD expansion -->

  
  <xd:doc> Expand one feature for given mode <xd:param name="n" type="string">Position in MSD
      string, category=0.
</xd:param>
    
<xd:param name="cat" type="string">Category key for table.</xd:param>
    
<xd:param name="codes" type="string">MSD suffix to process.</xd:param>
    
<xd:param name="mode" type="string">Output mode.</xd:param>
  
</xd:doc>

  
<xsl:template name="expand-feat-mode">
    
<xsl:param name="n" />
    
<xsl:param name="cat" />
    
<xsl:param name="codes" />
    
<xsl:param name="mode" />
    
<xsl:if test="normalize-space($codes) or (($canonical='cat' or $mode='collate') and //tei:table[@n='msd.cat'] [tei:row[@role='type']/tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-in]=$cat]/tei:row[@role='attribute']/tei:cell[@role='position']=$n )">
      
<!--xsl:if test="normalize-space($codes) or
        (($canonical='cat' or $mode='collate') and 
        key('cattable',$cat)/tei:row[@role='attribute']/tei:cell[@role='position']=$n
        )"
-->
      
<xsl:apply-templates mode="expand" select="//tei:table[@n='msd.cat'][tei:row[@role='type']/tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-in]=$cat]/tei:row[tei:cell[@role='position']=$n]">
        
<xsl:with-param name="code" select="substring($codes,1,1)" />
        
<xsl:with-param name="mode" select="$mode" />
      
</xsl:apply-templates>
      
<xsl:call-template name="expand-feat-mode">
        
<xsl:with-param name="n" select="$n+1" />
        
<xsl:with-param name="cat" select="$cat" />
        
<xsl:with-param name="codes" select="substring($codes,2)" />
        
<xsl:with-param name="mode" select="$mode" />
      
</xsl:call-template>
    
</xsl:if>
  
</xsl:template>

  
  <xd:doc> Expand category value. 
    
<xd:param name="code" type="string">Category code to process.</xd:param>
    
<xd:param name="mode" type="string">Output mode.</xd:param>
  
</xd:doc>

  
<xsl:template mode="expand" match="tei:row[@role='type']">
    
<xsl:param name="code" />
    
<xsl:param name="mode" />
    
<xsl:choose>
      
<xsl:when test="$mode='msd'">
        
<xsl:value-of select="tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
      
</xsl:when>
      
<xsl:when test="$mode='collate'">
        
<xsl:apply-templates mode="position" select="//tei:table[@n='msd.cat']">
          
<xsl:with-param name="code" select="$code" />
        
</xsl:apply-templates>
        
<xsl:value-of select="tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
      
</xsl:when>
      
<xsl:otherwise>
        
<xsl:value-of select="tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
      
</xsl:otherwise>
    
</xsl:choose>
    
<xsl:value-of select="$secondary-separator" />
  
</xsl:template>

  
  <xd:doc>Expand one feature. 
    
<xd:param name="code" type="string">Value code to process.</xd:param>
    
<xd:param name="mode" type="string">Output mode.</xd:param>
  
</xd:doc>

  
<xsl:template mode="expand" match="tei:row[@role='attribute']">
    
<xsl:param name="code" />
    
<xsl:param name="mode" />
    
<xsl:choose>
      
<xsl:when test="not(normalize-space($code))">
        
<xsl:if test="$canonical='cat' or $canonical='full' or $mode='collate'">
          
<xsl:call-template name="expand">
            
<xsl:with-param name="code">-</xsl:with-param>
            
<xsl:with-param name="mode" select="$mode" />
          
</xsl:call-template>
        
</xsl:if>
      
</xsl:when>
      
<xsl:when test="$code = '-'">
        
<xsl:if test="$canonical='cat' or $canonical='full' or $mode='collate' or $mode='msd'">
          
<xsl:call-template name="expand">
            
<xsl:with-param name="code">-</xsl:with-param>
            
<xsl:with-param name="mode" select="$mode" />
          
</xsl:call-template>
        
</xsl:if>
      
</xsl:when>
      
<xsl:otherwise>
        
<xsl:call-template name="expand">
          
<xsl:with-param name="code" select="$code" />
          
<xsl:with-param name="mode" select="$mode" />
        
</xsl:call-template>
      
</xsl:otherwise>
    
</xsl:choose>
  
</xsl:template>

  
<xsl:template name="expand">
    
<xsl:param name="code" />
    
<xsl:param name="mode" />
    
<xsl:choose>
      
<xsl:when test="$code = '-'">
        
<xsl:call-template name="not-applicable">
          
<xsl:with-param name="mode" select="$mode" />
          
<xsl:with-param name="att" select="tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
        
</xsl:call-template>
      
</xsl:when>
      
<xsl:otherwise>
        
<xsl:if test="$mode='att' or $mode='attval'">
          
<xsl:value-of select="tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
        
</xsl:if>
        
<xsl:if test="$mode='attval'">
          
<xsl:text>=</xsl:text>
        
</xsl:if>
        
<xsl:if test="$mode='msd' or $mode='collate' or $mode='val' or $mode='attval' or $mode='brief'">
          
<xsl:apply-templates mode="expand" select="tei:cell/tei:table/tei:row[@role='value']">
            
<xsl:with-param name="code" select="$code" />
            
<xsl:with-param name="mode" select="$mode" />
          
</xsl:apply-templates>
        
</xsl:if>
      
</xsl:otherwise>
    
</xsl:choose>
    
<xsl:value-of select="$secondary-separator" />
  
</xsl:template>

  
<xsl:template name="not-applicable">
    
<xsl:param name="mode" />
    
<xsl:param name="att" />
    
<xsl:if test="$mode='msd'">
      
<xsl:text>-</xsl:text>
    
</xsl:if>
    
<xsl:if test="$mode='collate'">
      
<xsl:text>00</xsl:text>
    
</xsl:if>
    
<xsl:if test="$mode='brief'">
      
<xsl:text>0</xsl:text>
    
</xsl:if>
    
<xsl:if test="$mode='att' or $mode='attval' or $mode='brief'">
      
<xsl:value-of select="$att" />
    
</xsl:if>
    
<xsl:if test="$mode='attval'">
      
<xsl:text>=</xsl:text>
    
</xsl:if>
    
<xsl:if test="$mode='val' or $mode='attval'">
      
<xsl:text>0</xsl:text>
    
</xsl:if>
  
</xsl:template>
  
  <xd:doc> Expand attribute value. <xd:param name="code" type="string">Code to process.</xd:param>
    
<xd:param name="mode" type="string">Output mode.</xd:param>
  
</xd:doc>

  
<xsl:template mode="expand" match="tei:row[@role='value']">
    
<xsl:param name="code" />
    
<xsl:param name="mode" />
    
<xsl:if test="tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-in] = $code">
      
<xsl:choose>
        
<xsl:when test="$mode='msd'">
          
<xsl:value-of select="tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
        
</xsl:when>
        
<xsl:when test="$mode='collate'">
          
<xsl:apply-templates mode="position" select="parent::tei:table/tei:row[@role='value']">
            
<xsl:with-param name="code" select="$code" />
          
</xsl:apply-templates>
        
</xsl:when>
        
<xsl:when test="$mode='val' or $mode='attval'">
          
<xsl:value-of select="tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
        
</xsl:when>
        
<xsl:when test="$mode='brief'">
          
<xsl:variable name="binary" select="tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang='en']" />
          
<xsl:choose>
            
<xsl:when test="$binary = 'no' or $binary = 'yes'">
              
<xsl:if test="$binary = 'no'">-</xsl:if>
              
<xsl:if test="$binary = 'yes'">+</xsl:if>
              
<xsl:value-of select="ancestor::tei:row[@role='attribute']/ tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
            
</xsl:when>
            
<xsl:otherwise>
              
<xsl:value-of select="tei:cell[@role='name'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-out]" />
            
</xsl:otherwise>
          
</xsl:choose>
        
</xsl:when>
      
</xsl:choose>
      
<xsl:value-of select="$secondary-separator" />
    
</xsl:if>
  
</xsl:template>

  
<!-- return position number of PoS  -->
  
<xsl:template mode="position" match="tei:table[@n='msd.cat']">
    
<xsl:param name="code" />
    
<xsl:if test="tei:row[@role='type']/tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-in]=$code">
      
<xsl:number value="position()" format="01" />
    
</xsl:if>
  
</xsl:template>
  
<!-- return position number of value -->
  
<xsl:template mode="position" match="tei:row">
    
<xsl:param name="code" />
    
<xsl:if test="tei:cell[@role='code'][ancestor-or-self::tei:*[@xml:lang][1]/@xml:lang=$lang-in]=$code">
      
<xsl:number value="position()" format="01" />
    
</xsl:if>
  
</xsl:template>

</xsl:stylesheet>






































v