I am trying to learn to format XSL more efficiently. I can tell by the definition of duplicate variables and nearly identical templates that I did not create this efficiently at all. How could I have formed this better?
Here is my XSL: you can see all of it at http://wwwstage.samford.edu/calendar/ADCCancel.xsl
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:r25="http://www.collegenet.com/r25"
xmlns:r25fn="http://www.collegenet.com/r25/functions"
xmlns:xl="http://www.w3.org/1999/xlink"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
version="2.0">
<xsl:output
method="xml"
encoding="UTF-8"
version="1.0"/>
<xsl:template match="/">
<xsl:variable name="pubdate">
<xsl:value-of select="r25:events/@pubdate"/>
</xsl:variable>
<xsl:element name="CALENDAR_ENTERPRISE">
<xsl:attribute name="Created">
<xsl:value-of select="$pubdate"/>
</xsl:attribute>
<xsl:element name="TIMEZONE">
<xsl:attribute name="type">group</xsl:attribute>
<xsl:element name="GMTOffset">
<xsl:attribute name="type">text</xsl:attribute>
<xsl:text>-6</xsl:text>
</xsl:element>
</xsl:element>
<xsl:apply-templates select="r25:events/r25:event/r25:profile">
<xsl:sort select="r25:init_start_dt"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="r25:profile">
<xsl:variable name="evtname" select="../r25:event_name"/>
<xsl:variable name="evttitle" select="../r25:event_title"/>
<xsl:variable name="refno" select="../r25:event_locator"/>
<xsl:variable name="profilename" select="r25:profile_name"/>
<xsl:variable name="desc" select="../r25:event_text[r25:text_type_id=1]/r25:text"/>
<xsl:variable name="relweb" select="../r25:custom_attribute[r25:attribute_id=-1]/r25:attribute_value"/>
<xsl:variable name="eventImage" select="../r25:custom_attribute[r25:attribute_id=-4]/r25:attribute_value"/>
<xsl:variable name="ticket" select="../r25:custom_attribute[r25:attribute_id=4]/r25:attribute_value"/>
<xsl:variable name="flyer" select="../r25:custom_attribute[r25:attribute_id=6]/r25:attribute_value"/>
<xsl:variable name="contact" select="../r25:role[r25:role_id = -1]/r25:contact"/>
<xsl:variable name="catsno" select="count(../r25:category)"/>
<xsl:variable name="org" select="../r25:organization[r25:primary='T']/r25:organization_name"/>
<xsl:variable name="created_dt" select="../r25:event_history[1]/r25:history_dt"/>
<xsl:variable name="usetitle" select="../r25:custom_attribute[r25:attribute_id=6]/r25:attribute_value"/>
<xsl:choose>
<xsl:when test="r25:rec_type_id != 2">
<xsl:call-template name="RecurringEvent">
<xsl:with-param name="evtname" select="$evtname"/>
<xsl:with-param name="evttitle" select="$evttitle"/>
<xsl:with-param name="refno" select="$refno"/>
<xsl:with-param name="profilename" select="$profilename"/>
<xsl:with-param name="desc" select="$desc"/>
<xsl:with-param name="relweb" select="$relweb"/>
<xsl:with-param name="ticket" select="$ticket"/>
<xsl:with-param name="flyer" select="$flyer"/>
<xsl:with-param name="evtimg" select="$eventImage"/>
<xsl:with-param name="contact" select="$contact"/>
<xsl:with-param name="catsno" select="$catsno"/>
<xsl:with-param name="org" select="$org"/>
<xsl:with-param name="startdate" select="r25:init_start_dt"/>
<xsl:with-param name="enddate" select="r25:init_end_dt"/>
<xsl:with-param name="spaces" select="r25:reservation/r25:space_reservation"/>
<xsl:with-param name="created" select="$created_dt"/>
<xsl:with-param name="usetitle" select="$usetitle"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="r25:reservation">
<xsl:with-param name="evtname" select="$evtname"/>
<xsl:with-param name="evttitle" select="$evttitle"/>
<xsl:with-param name="refno" select="$refno"/>
<xsl:with-param name="profilename" select="$profilename"/>
<xsl:with-param name="desc" select="$desc"/>
<xsl:with-param name="relweb" select="$relweb"/>
<xsl:with-param name="evtimg" select="$eventImage"/>
<xsl:with-param name="ticket" select="$ticket"/>
<xsl:with-param name="flyer" select="$flyer"/>
<xsl:with-param name="contact" select="$contact"/>
<xsl:with-param name="catsno" select="$catsno"/>
<xsl:with-param name="org" select="$org"/>
<xsl:with-param name="created" select="$created_dt"/>
<xsl:with-param name="usetitle" select="$usetitle"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="RecurringEvent">
<!-- Setup variables for different parts of the XML for each event -->
<xsl:param name="evtname"/>
<xsl:param name="evttitle"/>
<xsl:param name="refno"/>
<xsl:param name="profilename"/>
<xsl:param name="desc"/>
<xsl:param name="relweb"/>
<xsl:param name="ticket"/>
<xsl:param name="flyer"/>
<xsl:param name="evtimg"/>
<xsl:param name="contact"/>
<xsl:param name="catsno"/>
<xsl:param name="org"/>
<xsl:param name="startdate"/>
<xsl:param name="laststartdate"/>
<xsl:param name="enddate"/>
<xsl:param name="spaces"/>
<xsl:param name="created"/>
<xsl:param name="usetitle"/>
XSL TO CREATE EVENTNODES
</xsl:template>
<xsl:template match="r25:reservation">
<!-- Setup variables for different parts of the XML for each event -->
<xsl:param name="evtname"/>
<xsl:param name="evttitle"/>
<xsl:param name="refno"/>
<xsl:param name="profilename"/>
<xsl:param name="desc"/>
<xsl:param name="relweb"/>
<xsl:param name="evtimg"/>
<xsl:param name="ticket"/>
<xsl:param name="flyer"/>
<xsl:param name="contact"/>
<xsl:param name="catsno"/>
<xsl:param name="org"/>
<xsl:param name="created"/>
<xsl:variable name="startdate" select="r25:event_start_dt"/>
<xsl:variable name="enddate" select="r25:event_end_dt"/>
<xsl:variable name="spaces" select="r25:space_reservation"/>
/// xsl to create Event Nodes
</xsl:template>
<xsl:template match="r25:category">
<xsl:variable name="goodcategories">
<!-- Only push marketing categories to the calendar. The list below represents their IDs -->
<xsl:variable name="isinlist">
<xsl:value-of select="index-of((31,32,33,35,43,46,101,104,105,106,107,108,125,125,127,128,129),number(r25:category_id))" />
</xsl:variable>
<xsl:if test="$isinlist != ''">
<xsl:value-of select="concat(r25:category_name,'||')"/>
</xsl:if>
</xsl:variable>
<xsl:value-of select="$goodcategories"/>
</xsl:template>
<xsl:template match="r25:rec_type_id">
<!-- This translates the R25 recurrence ID into something ADC understands based on http://knowledge25.collegenet.com/display/WSW/Profile+information-->
<xsl:param name="code"/>
<xsl:variable name="pattern" select="substring($code,1,2)"/>
<xsl:element name="RecurType">
<xsl:choose>
<!--rec type of 0 means there are no recurrences. it is just a one-off event -->
<xsl:when test=". = 0">
<xsl:text>One Time</xsl:text>
</xsl:when>
<!--rec type of 2 means there are is a meeting pattern. the first 2 letters define the pattern. -->
<xsl:when test=". = 1">
<xsl:choose>
<xsl:when test="substring($pattern,1,1) = 'W'">
<xsl:text>Weekly</xsl:text>
<xsl:value-of select="substring($pattern,2,1)"/>
</xsl:when>
<!--Set if monthly-->
<xsl:when test="substring($pattern,1,1) = 'M'">
<xsl:text>Monthly by Date</xsl:text>
<!--Unlike the others, the monthly patterns list the number on the 3rd character -->
<xsl:value-of select="substring($code,3,1)"/>
</xsl:when>
<!--Set if daily-->
<xsl:when test="substring($pattern,1,1) = 'D'">
<xsl:text>Interval</xsl:text>
<xsl:value-of select="substring($pattern,2,1)"/>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test=". = 2">
<xsl:text>Custom</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>Otherwise</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
<xsl:element name="RecurEndDate">
<xsl:attribute name="type">text</xsl:attribute>
<xsl:call-template name="parse-profile-code">
<xsl:with-param name="ProfileCode" select="$code"/>
<xsl:with-param name="return">Date</xsl:with-param>
</xsl:call-template>
</xsl:element>
<xsl:element name="RecurDays">
<xsl:attribute name="type">text</xsl:attribute>
<!-- The following if statement is to take into account the rule mentioned on pg 8
of the import guide stating this is only supposed to be used if the recur type is weekly -->
<xsl:if test="substring($pattern,1,1) = 'W'">
<xsl:call-template name="parse-profile-code">
<xsl:with-param name="ProfileCode" select="$code"/>
<xsl:with-param name="return">Days</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:element>
</xsl:template>
<xsl:template name="parse-profile-code">
<xsl:param name="ProfileCode"/>
<xsl:param name="return"/>
<xsl:analyze-string select="$ProfileCode" regex="^([DWMY])([MDP]?)([0-9]*)\s?(.*)?">
<xsl:matching-substring>
<xsl:variable name="Rest">
<xsl:value-of select="regex-group(4)"/>
</xsl:variable>
<xsl:variable name="Until">
<!-- Build the recur end date from the R25 profile code and store in a variable named until-->
<xsl:choose>
<xsl:when test="$return='Date'">
<xsl:analyze-string select="$Rest" regex="([0-9][0-9]*T[0-9]*([\-+][0-9]*|Z)?|#[0-9]*)">
<xsl:matching-substring>
<!--The profile code lacks the necessary dashes to perform the format dateTime function. This inserts them in where needed. -->
<xsl:value-of select="substring(regex-group(1),1,4)"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="substring(regex-group(1),5,2)"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="substring(regex-group(1),7,2)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:when>
<xsl:when test="$return='Days'">
<xsl:analyze-string select="$Rest" regex="([0-9][0-9]*T[0-9]*([\-+][0-9]*|Z)?|#[0-9]*)">
<xsl:matching-substring>
<xsl:value-of select="normalize-space(regex-group(1))"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="Days">
<xsl:choose>
<xsl:when test="string-length($Until) = 0">
<xsl:value-of select="$Rest"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-before($Rest,$Until))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$return = 'Date'">
<xsl:value-of select="format-date($Until,'[M]/[D01]/[Y]')"/>
</xsl:when>
<xsl:when test="$return = 'Days'">
<xsl:call-template name="listdays">
<xsl:with-param name="daylist">
<xsl:value-of select="$Days"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template name="listdays">
<xsl:param name="daylist"/>
<xsl:variable name="tokenizedDayList" select="tokenize($daylist,'\s+')"/>
<xsl:for-each select="$tokenizedDayList">
<xsl:if test=". = 'MO'">Monday</xsl:if>
<xsl:if test=". = 'TU'">Tuesday</xsl:if>
<xsl:if test=". = 'WE'">Wednesday</xsl:if>
<xsl:if test=". = 'TH'">Thursday</xsl:if>
<xsl:if test=". = 'FR'">Friday</xsl:if>
<xsl:if test=". = 'SA'">Saturday</xsl:if>
<xsl:if test=". = 'SU'">Sunday</xsl:if>
<xsl:if test=". != $tokenizedDayList[last()]">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
Here is a sample of the xml file being transformed: http://wwwstage.samford.edu/calendar/sampler25.xml
And here is a sample of the output I am producing: http://wwwstage.samford.edu/calendar/ADECancel.aspx
1 Answer 1
Some tips to improve your coding:
(a) don't do this:
<xsl:variable name="pubdate">
<xsl:value-of select="r25:events/@pubdate"/>
</xsl:variable>
when you can do this:
<xsl:variable name="pubdate" select="r25:events/@pubdate"/>
The latter is not only one line of code instead of three, it's also much more efficient because it doesn't involve constructing a new tree.
(b) don't do this:
<xsl:element name="CALENDAR_ENTERPRISE">
<xsl:attribute name="Created">
<xsl:value-of select="$pubdate"/>
</xsl:attribute>
when you can do this:
<CALENDAR_ENTERPRISE Created="{$pubdate}">
In Saxon it won't make any performance difference, but it's much more readable.
(c) I can't see why you are passing all these parameters. The values are all accessible from the context node, so I don't see why the called template can't get the values it needs by navigating from the context node.