Skip to content

Instantly share code, notes, and snippets.

@xymox12
Created November 20, 2013 21:08
Show Gist options
  • Save xymox12/7571057 to your computer and use it in GitHub Desktop.
Save xymox12/7571057 to your computer and use it in GitHub Desktop.
<?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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment