<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:ical="http://www.ietf.org/rfc/rfc2445.txt"
	xmlns:itip="http://www.ietf.org/rfc/rfc2446.txt"
	xmlns="http://www.ietf.org/rfc/rcf2445.txt">
<!-- ======================================================================== -->
<!-- ics2xml.xsl XSL transformation of iCalendar .ics file to XML file        -->
<!-- Tim Hare, November 2006                                                  -->  
<!-- This transformation may be freely used, attribution is appreciated       -->
<!-- ======================================================================== -->


<!-- define output format                                                     -->
<xsl:output method="xml" media-type="xml/calendar" indent="yes" />

<!--                                                                          -->
<!-- global parameters of use throughout the stylesheet                       -->
<!--                                                                          -->
<xsl:variable name="newline"><xsl:text>
</xsl:text></xsl:variable>
<xsl:variable name="linewrap"><xsl:text>
 </xsl:text></xsl:variable>
<xsl:variable name="CR">&#13;</xsl:variable>
<xsl:variable name="colon"><xsl:text>:</xsl:text></xsl:variable>
<xsl:variable name="semicolon"><xsl:value-of select="concat('&#59;','')" /></xsl:variable>


 
<!--<xsl:apply-templates select="*" />-->
<xsl:template match="/">
   <xsl:element name="iCalendar_Stream">
   <xsl:call-template name="convert_ics">
      <!-- "parameter" is the entire input file -->
      <xsl:with-param name="icaldata">
         <!--                                                                  -->
         <!-- First we adjust the whitespace in the incoming document .ics     -->
         <!-- file. Can NOT use normalize-space() or document() because they   -->
         <!-- remove the the CR-LF sequence crucial to recognizing the end of  -->
         <!-- things in iCalendar                                              -->
         <!--                                                                  -->
         <xsl:call-template name="space_adjust">
            <xsl:with-param name="icsinput">
               <xsl:value-of select="current()" />
            </xsl:with-param>
         </xsl:call-template>
      </xsl:with-param>
   </xsl:call-template>
   </xsl:element>
</xsl:template>

<!-- Process recursively.                                       -->
<!-- Find Calendars and Components (BEGIN/END pairs)            -->
<!-- and then process properties with parameters                -->
<!-- try find BEGIN: then extract name and search for END:$name -->
<xsl:template name="convert_ics">
   <xsl:param name="icaldata" />
   <xsl:choose>
      <xsl:when test="$icaldata = $newline">
         <!-- do nothing if it's only newline -->
      </xsl:when>
      <xsl:when test="starts-with($icaldata,'BEGIN:')">
          <xsl:variable name="element_name">
             <xsl:value-of select="substring-before(substring-after($icaldata,'BEGIN:'),$newline)" />
          </xsl:variable>
          <xsl:choose>
             <xsl:when test="substring-before($icaldata,concat('END:',$element_name))">
                <xsl:value-of select="$newline" />
                <xsl:element name="{concat($element_name,'')}">
                   <xsl:call-template name="convert_ics">
                      <xsl:with-param name="icaldata">
                          <xsl:value-of select="substring-before(substring-after($icaldata,concat('BEGIN:',$element_name,$newline)),concat('END:',$element_name,$newline))" />
                      </xsl:with-param>
                   </xsl:call-template>
                </xsl:element>
             </xsl:when>
             <xsl:otherwise>
             </xsl:otherwise>
          </xsl:choose>
          <xsl:choose>
             <xsl:when test="substring-after($icaldata,concat('END:',$element_name,$newline))" >
                <xsl:call-template name="convert_ics">
                   <xsl:with-param name="icaldata">
                      <xsl:value-of select="substring-after($icaldata,concat('END:',$element_name,$newline))" />
                   </xsl:with-param>
                </xsl:call-template>
             </xsl:when>
             <xsl:otherwise>
             </xsl:otherwise>
          </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
            <!-- It isn't a BEGIN/END pair, so it's a property                                  -->
            <!-- each property becomes an element, each parameter an attribute of it            -->
            <xsl:variable name="element_name">
               <xsl:choose>
                  <!-- if there's a semicolon, then the name is the string before the semicolon -->
                  <xsl:when test="contains(substring-before($icaldata,$colon),$semicolon)" >
                     <xsl:value-of select="substring-before($icaldata,$semicolon)" />
                  </xsl:when>
                  <!-- otherwise the name is the string before the colon -->
                  <xsl:otherwise>
                     <xsl:value-of select="substring-before($icaldata,$colon)" />
                  </xsl:otherwise>
               </xsl:choose>
            </xsl:variable>
            <!--                                                                                -->
            <!--         Now create the elements                                                -->
            <!--                                                                                -->
            <xsl:value-of select="$newline" />
            <xsl:element name="{string($element_name)}" >
               <!--                                                                             -->
               <!--      If there is a semicolon, then we have property parameters which we     -->
               <!--      need to make into attributes of the element                            -->
               <!--                                                                             -->
               <xsl:if test="contains(substring-before($icaldata,$colon),$semicolon)" >
                  <xsl:call-template name="process_parameters">
                     <xsl:with-param name="parameters">
                        <!--- everything from the first semicolon to the colon are properties   -->
                        <xsl:value-of select="substring-after(substring-before($icaldata,$colon),$semicolon)" />
                     </xsl:with-param>
                  </xsl:call-template>
               </xsl:if>
               <!--                                                                             -->
               <!--  contents of the element are everything after the colon                     -->
               <!--  precede it with white space, to make it look better                        -->
               <!--                                                                             -->
               <xsl:value-of select="substring-before(substring-after($icaldata,$colon),$newline)" />
            </xsl:element>
            <xsl:if test="substring-after($icaldata,$newline)">
               <xsl:call-template name="convert_ics">
                  <xsl:with-param name="icaldata">
                     <xsl:value-of select="substring-after($icaldata,$newline)" />
                  </xsl:with-param>
               </xsl:call-template>
            </xsl:if>
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>

<xsl:template name="process_parameters">
   <!--- recursively strip out parameters and make attributes of them -->
   <xsl:param name="parameters" />
   <xsl:choose>
      <!-- if more than one parameter present they are separated by semicolons -->
      <xsl:when test="substring-before($parameters,$semicolon)">
         <xsl:attribute name="{substring-before(substring-before($parameters,$semicolon),'=')}">
            <xsl:value-of select="substring-after(substring-before($parameters,$semicolon),'=')" />
         </xsl:attribute>
         <xsl:call-template name="process_parameters">
            <xsl:with-param name="parameters">
               <xsl:value-of select="substring-after($parameters,$semicolon)" />
            </xsl:with-param>
         </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
          <xsl:if test="contains($parameters,'=')">
             <xsl:attribute name="{substring-before($parameters,'=')}">
                <xsl:value-of select="substring-after($parameters,'=')" />
             </xsl:attribute>
          </xsl:if>
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>

<xsl:template name="space_adjust">
   <!--                                                                                           -->
   <!-- This template removes leading spaces, and line-wrap sequences from the input, returning a -->
   <!-- variable which may still contain CR-LF sequences but not CR-LF-space                      -->
   <!-- it is invoked once as the entire iCalendar input file is turned into a variable           -->
   <xsl:param name="icsinput" />
   <xsl:variable name="special_case"><xsl:value-of select="concat($newline,'BEGIN:')" /></xsl:variable>
   <xsl:choose>
      <xsl:when test="starts-with($icsinput,$special_case)">              <!--  Special case      -->
         <xsl:call-template name="space_adjust">
            <xsl:with-param name="icsinput">
               <xsl:value-of select="substring-after($icsinput,$newline)" /> <!-- strip leading NL -->
            </xsl:with-param>
         </xsl:call-template>
      </xsl:when>
      <!--                                                                                        -->
      <!-- To hold down recursion depth and iterations, test for different lengths of leading     -->
      <!-- spaces before recursively calling this routine                                         -->
      <xsl:when test="starts-with($icsinput,'     ')"> <!--  five spaces                          -->
         <xsl:call-template name="space_adjust">
            <xsl:with-param name="icsinput">
               <xsl:value-of select="substring-after($icsinput,'     ')" /> <!-- strip five       -->
            </xsl:with-param>
         </xsl:call-template>
      </xsl:when>
      <xsl:when test="starts-with($icsinput,'    ')">  <!--  four spaces                          -->
         <xsl:call-template name="space_adjust">
            <xsl:with-param name="icsinput">
               <xsl:value-of select="substring-after($icsinput,'    ')" /> <!-- strip four        -->
            </xsl:with-param>
         </xsl:call-template>
      </xsl:when>
      <xsl:when test="starts-with($icsinput,'   ')">   <!--  three spaces                         -->
         <xsl:call-template name="space_adjust">
            <xsl:with-param name="icsinput">
               <xsl:value-of select="substring-after($icsinput,'   ')" />  <!-- strip three       -->
            </xsl:with-param>
         </xsl:call-template>
      </xsl:when>
      <xsl:when test="starts-with($icsinput,'  ')">    <!--  two spaces                           -->
         <xsl:call-template name="space_adjust">
            <xsl:with-param name="icsinput">
               <xsl:value-of select="substring-after($icsinput,'  ')" />   <!-- strip two         -->
            </xsl:with-param>
         </xsl:call-template>
      </xsl:when>
      <xsl:when test="starts-with($icsinput,' ')">     <!--  one space                            -->
         <xsl:call-template name="space_adjust">
            <xsl:with-param name="icsinput">
               <xsl:value-of select="substring-after($icsinput,' ')" />    <!-- strip one         -->
            </xsl:with-param>
         </xsl:call-template>
      </xsl:when>
      <!--                                                                                        -->
      <!-- At this point all leading spaces should be gone, so we look for linewraps to remove    -->
      <!--                                                                                        -->
      <xsl:when test="substring-before($icsinput,$linewrap)">
          <xsl:call-template name="space_adjust">
             <xsl:with-param name="icsinput">
                <!-- replace the linewrap with a single space                                     -->
                <xsl:value-of select="concat(substring-before($icsinput,$linewrap),' ',substring-after($icsinput,$linewrap))" />
             </xsl:with-param>
           </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
         <xsl:value-of select="$icsinput" />
      </xsl:otherwise>  
   </xsl:choose>
</xsl:template>
</xsl:stylesheet>
