GitBucket
4.21.2
Toggle navigation
Snippets
Sign in
Files
Branches
1
Releases
1
Issues
Pull requests
Labels
Priorities
Milestones
Wiki
Forks
nigel.stanger
/
XML
Browse code
Merge branch 'master' of https://github.com/Otago-InfoSci-Database/XML
master
commit
e103ddc2e4a040aa36a5d4b7495cfe334d635db3
2 parents
d32ea68
+
738f8ef
Nigel Stanger
authored
on 24 Feb 2015
Patch
Showing
2 changed files
generate_calendar_dates.php
xml2xslt.xsl
Ignore Space
Show notes
View
generate_calendar_dates.php
<?php /* This script generates an XSLT include file that contains templates specifying the date ranges for every week of the three main teaching periods through the year (i.e., summer school, first semester, second semester). Simply set the configuration as outlined below and make to regenerate the templates. */ /* Date of the Monday of the first academic week of the year. Teaching weeks start on Monday, so this is a convenient base point to start from. This will need to be updated annually. */ $first_monday = new DateTime( "2014-12-29" ); /* Teaching period configuration. This will need to be updated annually. It's easily extensible to a new teaching period simply by adding a new specification for that period. The key should be mixed-case alphanumeric (i.e., no whitespace, no punctuation or other special characters). The components of each period specification are: first_week: The academic week number of the first week of the period. last_week: The academic week number of the last week of the period. break_weeks: A list of breaks that occur during the teaching period. Each is specified by the start (break_start) and end (break_end) week. If a break is only one week long, then break_start and break_end should be equal. If there are no breaks in the teaching period, include a single break specification with both break_start and break_end set to zero. The list should ideally be in ascending order, but the script doesn't assume this and sorts the list anyway. Weeks are numbered by academic week number rather than week within the teaching period. */ $periods = array( 'SS' => array( 'first_week' => 2, 'last_week' => 7, 'break_weeks' => array( array( 'break_starts' => 0, 'break_ends' => 0, ), ), ), 'S1' => array( 'first_week' => 9, 'last_week' => 22, 'break_weeks' => array( array( 'break_starts' => 15, 'break_ends' => 15, ), ), ), 'S2' => array( 'first_week' => 28, 'last_week' => 41, 'break_weeks' => array( array( 'break_starts' => 35, 'break_ends' => 35, ), ), ), 'FY' => array( 'first_week' => 9, 'last_week' => 41, 'break_weeks' => array( array( 'break_starts' => 15, 'break_ends' => 15, ), array( 'break_starts' => 23, 'break_ends' => 27, ), array( 'break_starts' => 35, 'break_ends' => 35, ), ), ), ); // We need a condition to validate the period code in the templates. $period_condition = sprintf( "( @period = '%s' )", implode( "' ) or ( @period = '", array_keys( $periods ) ) ); // We also a list of valid period code values for the error string. $period_list = sprintf( '"%s"', implode( '", "', array_keys( $periods ) ) ); // The period and week variables are defined identically in both templates, so // let's define them just once here to ensure consistency. $shared_parameters = <<<EOT <xsl:with-param name="period"> <xsl:choose> <xsl:when test="{$period_condition}"> <xsl:value-of select="@period" /> </xsl:when> <!-- This also covers the case of @period being undefined. --> <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Attribute "period" must be one of the values {$period_list}.</xsl:text> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:with-param> <xsl:with-param name="week"> <xsl:value-of select="@week" /> <xsl:if test="not( @week )"> <xsl:message terminate="yes"> <xsl:text>The "week" attribute is required.</xsl:text> </xsl:message> </xsl:if> </xsl:with-param> EOT; // Generate the file header and the wrapper templates. print<<<EOT <?xml version="1.0" encoding="utf-8"?> <!-- DO NOT EDIT! Automatically generated by ../generate_calendar_dates.php! Elements and templates for generating teaching period dates. Apart from the wrapper templates, this essentially boils down to a giant parameterised lookup table, which is generated by a script. --> <stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Generate a date for a given teaching week in a teaching period, in the specified format. Return the date of the Monday of the week, plus an optional signed offset. @period: The teaching period for which to generate the date. [required] @week: The number of the week in the specified teaching period. [required] @format: How to format the date. 'day+long-name' => [FNn] [D] [MNn] (e.g., Friday 10 August) 'day+short-name' => [FNn,*-3] [D] [MNn] (e.g., Fri 10 August) 'day' => [D] [MNn] (e.g., 10 August) 'day-short' => [D] [MNn,*-3] (e.g., 10 Aug) 'day+year' => [D] [MNn], [Y] (e.g., 10 August, 2013) 'day-short+year' => [D] [MNn,*-3], [Y] (e.g., 10 Aug, 2013) 'ISO' => [Y0001]-[M01]-[D01] (e.g., 2012-08-10) date picture string => Any valid XSLT date picture specification (see http://www.w3.org/TR/xslt20/#date-picture-string). @offset: A signed offset specified as an XSLT duration, e.g., "P1D" for plus one day, "-P4D" for minus four days. --> <template name="TeachingPeriodDate" match="TeachingPeriodDate"> <common> <xsl:call-template name="format-teaching-date"> {$shared_parameters} <xsl:with-param name="format"> <xsl:choose> <!-- Named date formats. --> <xsl:when test="@format = 'day+long-name'"> <xsl:text>[FNn] [D] [MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'day+short-name'"> <xsl:text>[FNn,*-3] [D] [MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'day'"> <xsl:text>[D] [MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'day-short'"> <xsl:text>[D] [MNn,*-3]</xsl:text> </xsl:when> <xsl:when test="@format = 'day+year'"> <xsl:text>[D] [MNn], [Y]</xsl:text> </xsl:when> <xsl:when test="@format = 'day-short+year'"> <xsl:text>[D] [MNn,*-3], [Y]</xsl:text> </xsl:when> <xsl:when test="@format = 'ISO'"> <xsl:text>[Y0001]-[M01]-[D01]</xsl:text> </xsl:when> <xsl:otherwise> <xsl:value-of select="@format" /> </xsl:otherwise> </xsl:choose> <xsl:if test="not( @format )"> <xsl:text>[Y0001]-[M01]-[D01]</xsl:text> </xsl:if> </xsl:with-param> <xsl:with-param name="offset"> <xsl:value-of select="@offset" /> <xsl:if test="not( @offset )"> <xsl:text>P0D</xsl:text> </xsl:if> </xsl:with-param> </xsl:call-template> </common> </template> <!-- Generate a date range corresponding to a given teaching week in a teaching period (i.e., Monday to Friday). @period: The teaching period for which to generate the date. [required] @week: The number of the week in the specified teaching period. [required] @format: How to format the month name in the date range. 'long' => Output the full month name (e.g., August) 'short' => Output only the first three characters of the month name (e.g., Aug) @wrap: Whether to wrap the date range across two lines (usually for insertion into a narrow cell in a calendar table). 'true' [default] 'false' --> <template name="TeachingPeriodDateRange" match="TeachingPeriodDateRange"> <common> <xsl:call-template name="format-teaching-date-range"> {$shared_parameters} <xsl:with-param name="month-format"> <xsl:choose> <xsl:when test="@format = 'long'"> <xsl:text>[MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'short'"> <xsl:text>[MNn,*-3]</xsl:text> </xsl:when> <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Attribute "format" must be either "long" or "short", not "</xsl:text> <xsl:value-of select="@format" /> <xsl:text>".</xsl:text> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:with-param> <!-- Convert @wrap into a boolean value. --> <xsl:with-param name="wrap"> <xsl:value-of select="@wrap = 'yes'" /> </xsl:with-param> </xsl:call-template> </common> </template> EOT; // Generate the XSLT templates that do all the work. These essentially end up being huge parameterised lookup tables. generate_template( $first_monday, $periods, 'format-teaching-date' ); generate_template( $first_monday, $periods, 'format-teaching-date-range' ); // Footer boilerplate. print( "</stylesheet>\n" ); exit; //////////////////////////////////////////////////////////////////////////////// function generate_template( $first_monday, $periods, $template_name ) { // A couple of handy date intervals: four days to the end of the current week, // and seven days to the start of next week. $plus_four_days = new DateInterval( 'P4D' ); $plus_seven_days = new DateInterval( 'P7D' ); // Initial boilerplate for the template. switch ( $template_name ) { case 'format-teaching-date': print<<<EOT <!-- Generate a date (default Monday) for the specified teaching period and week (or break), using the specified date format, with an optional offset from the Monday of the week. Most of the body of this template is automatically generated by the script ../generate_calendar_dates.php. See the attributes of the TeachingPeriodDate template above for descriptions of the parameters. --> <template name="format-teaching-date"> <common> <xsl:param name="period" as="xs:string" /> <xsl:param name="week" as="xs:string" /> <xsl:param name="format" as="xs:string" /> <xsl:param name="offset" as="xs:dayTimeDuration" /> <xsl:choose> EOT; break; case 'format-teaching-date-range': print<<<EOT <!-- Generate a date range for the specified teaching period and week (or break), using the specified month format, with optional wrapping. Most of the body of this template is automatically generated by the script ../generate_calendar_dates.php. See the attributes of the TeachingPeriodDateRange template above for descriptions of the parameters. \$month-format is derived from @format. --> <template name="format-teaching-date-range"> <common> <xsl:param name="period" as="xs:string" /> <xsl:param name="week" as="xs:string" /> <xsl:param name="month-format" as="xs:string" /> <xsl:param name="wrap" as="xs:boolean" /> <xsl:choose> EOT; break; default: // WTF?!? print "Unrecognised template name \"$template_name\", terminating.\n"; exit; break; } // switch template name // Generate XSLT code for each teaching period. foreach ( $periods as $period_name => $period_data ) { print<<<EOT <xsl:when test="\$period = '{$period_name}'"> <xsl:choose> EOT; $week_start = clone( $first_monday ); // Jump forward the correct number of weeks to the start of the teaching period. $week_start->add( new DateInterval( sprintf( "P%dD", ( $period_data['first_week'] - 1 ) * 7 ) ) ); $week_number = 0; $num_breaks = 0; // Grab the first break week off the front of the list. sort( $period_data['break_weeks'] ); $break_week = array_shift( $period_data['break_weeks'] ); // Generate XSLT code for each teaching week and breaks within the period. for ( $week = $period_data['first_week']; $week <= $period_data['last_week']; $week++ ) { $week_end = clone( $week_start ); $week_end->add( $plus_four_days ); // Handle breaks. if ( ( $week >= $break_week['break_starts'] ) && ( $week <= $break_week['break_ends'] ) ) { if ( $week == $break_week['break_starts'] ) { $num_breaks++; // Work out the end date of the break. $break_end = clone( $week_start ); // Note: +4 days to get to Friday. $break_end->add( new DateInterval( sprintf( "P%dD", ( ( $break_week['break_ends'] - $break_week['break_starts'] ) * 7 ) + 4 ) ) ); switch ( $template_name ) { case 'format-teaching-date': generate_date( "B{$num_breaks}", $week_start ); break; case 'format-teaching-date-range': if ( $week_start->format( 'n' ) == $break_end->format( 'n' ) ) { generate_same_month_date_range( "B{$num_breaks}", $week_start, $break_end ); } // if break start and break end are in the same month else { generate_different_month_date_range( "B{$num_breaks}", $week_start, $break_end ); } // else break start and break end are in different months break; default: // WTF?!? print "Unrecognised template name \"$template_name\", terminating.\n"; exit; break; } // switch template name } // if current week == first week of break if ( $week == $break_week['break_ends'] ) { // Grab the next break week off the front of the list. $break_week = array_shift( $period_data['break_weeks'] ); } // if current week == last week of break } // if current week is a break week else { $week_number++; switch ( $template_name ) { case 'format-teaching-date': generate_date( $week_number, $week_start ); break; case 'format-teaching-date-range': if ( $week_start->format( 'n' ) == $week_end->format( 'n' ) ) { generate_same_month_date_range( $week_number, $week_start, $week_end ); } // if break start and break end are in the same month else { generate_different_month_date_range( $week_number, $week_start, $week_end ); } // else break start and break end are in different months break; default: // WTF?!? print "Unrecognised template name \"$template_name\", terminating.\n"; exit; break; } // switch template name } // else normal teaching week $week_start->add( $plus_seven_days ); } // for each week // Terminating boilerplate for this teaching period. print<<<EOT <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Invalid {$period_name} week specification "</xsl:text> <xsl:value-of select="\$week" /> <xsl:text>".</xsl:text> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:when> EOT; } // foreach teaching period // Terminating boilerplate for the template. print<<<EOT </xsl:choose> </common> </template> EOT; } // generate_template //////////////////////////////////////////////////////////////////////////////// function generate_date( $week_spec, $date ) { print<<<EOT <xsl:when test="\$week = '{$week_spec}'"> <xsl:value-of select="format-date( xs:date( '{$date->format( 'Y-m-d' )}' ) + \$offset, \$format )" /> </xsl:when> EOT; } // generate_same_month_date_range //////////////////////////////////////////////////////////////////////////////// function generate_same_month_date_range( $week_spec, $start_date, $end_date ) { print<<<EOT <xsl:when test="\$week = '{$week_spec}'"> <xsl:value-of select="format-date( xs:date( '{$start_date->format( 'Y-m-d' )}' ), '[D]' )" /> <xsl:call-template name="endash" /> <xsl:value-of select="format-date( xs:date( '{$end_date->format( 'Y-m-d' )}' ), '[D] ' )" /> <xsl:if test="\$wrap"> <xsl:call-template name="newline" /> </xsl:if> <xsl:value-of select="format-date( xs:date( '{$end_date->format( 'Y-m-d' )}' ), \$month-format )" /> </xsl:when> EOT; } // generate_same_month_date_range function generate_different_month_date_range( $week_spec, $start_date, $end_date ) { print<<<EOT <xsl:when test="\$week = '{$week_spec}'"> <xsl:value-of select="format-date( xs:date( '{$start_date->format( 'Y-m-d' )}' ), concat( '[D] ', \$month-format ) )" /> <xsl:if test="\$wrap"> <xsl:call-template name="newline" /> </xsl:if> <xsl:call-template name="endash" /> <xsl:value-of select="format-date( xs:date( '{$end_date->format( 'Y-m-d' )}' ), concat( '[D] ', \$month-format ) )" /> </xsl:when> EOT; } // generate_different_month_date_range ?>
<?php /* This script generates an XSLT include file that contains templates specifying the date ranges for every week of the three main teaching periods through the year (i.e., summer school, first semester, second semester). Simply set the configuration as outlined below and make to regenerate the templates. */ /* Date of the Monday of the first academic week of the year. Teaching weeks start on Monday, so this is a convenient base point to start from. This will need to be updated annually. */ $first_monday = new DateTime( "2014-12-29" ); /* Teaching period configuration. This will need to be updated annually. It's easily extensible to a new teaching period simply by adding a new specification for that period. The key should be mixed-case alphanumeric (i.e., no whitespace, no punctuation or other special characters). The components of each period specification are: first_week: The academic week number of the first week of the period. last_week: The academic week number of the last week of the period. break_weeks: A list of breaks that occur during the teaching period. Each is specified by the start (break_start) and end (break_end) week. If a break is only one week long, then break_start and break_end should be equal. If there are no breaks in the teaching period, include a single break specification with both break_start and break_end set to zero. The list should ideally be in ascending order, but the script doesn't assume this and sorts the list anyway. Weeks are numbered by academic week number rather than week within the teaching period. */ $periods = array( 'SS' => array( 'first_week' => 2, 'last_week' => 7, 'break_weeks' => array( array( 'break_starts' => 0, 'break_ends' => 0, ), ), ), 'S1' => array( 'first_week' => 9, 'last_week' => 22, 'break_weeks' => array( array( 'break_starts' => 17, 'break_ends' => 17, ), ), ), 'S2' => array( 'first_week' => 28, 'last_week' => 41, 'break_weeks' => array( array( 'break_starts' => 35, 'break_ends' => 35, ), ), ), 'FY' => array( 'first_week' => 9, 'last_week' => 41, 'break_weeks' => array( array( 'break_starts' => 17, 'break_ends' => 17, ), array( 'break_starts' => 23, 'break_ends' => 27, ), array( 'break_starts' => 35, 'break_ends' => 35, ), ), ), ); // We need a condition to validate the period code in the templates. $period_condition = sprintf( "( @period = '%s' )", implode( "' ) or ( @period = '", array_keys( $periods ) ) ); // We also a list of valid period code values for the error string. $period_list = sprintf( '"%s"', implode( '", "', array_keys( $periods ) ) ); // The period and week variables are defined identically in both templates, so // let's define them just once here to ensure consistency. $shared_parameters = <<<EOT <xsl:with-param name="period"> <xsl:choose> <xsl:when test="{$period_condition}"> <xsl:value-of select="@period" /> </xsl:when> <!-- This also covers the case of @period being undefined. --> <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Attribute "period" must be one of the values {$period_list}.</xsl:text> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:with-param> <xsl:with-param name="week"> <xsl:value-of select="@week" /> <xsl:if test="not( @week )"> <xsl:message terminate="yes"> <xsl:text>The "week" attribute is required.</xsl:text> </xsl:message> </xsl:if> </xsl:with-param> EOT; // Generate the file header and the wrapper templates. print<<<EOT <?xml version="1.0" encoding="utf-8"?> <!-- DO NOT EDIT! Automatically generated by ../generate_calendar_dates.php! Elements and templates for generating teaching period dates. Apart from the wrapper templates, this essentially boils down to a giant parameterised lookup table, which is generated by a script. --> <stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Generate a date for a given teaching week in a teaching period, in the specified format. Return the date of the Monday of the week, plus an optional signed offset. @period: The teaching period for which to generate the date. [required] @week: The number of the week in the specified teaching period. [required] @format: How to format the date. 'day+long-name' => [FNn] [D] [MNn] (e.g., Friday 10 August) 'day+short-name' => [FNn,*-3] [D] [MNn] (e.g., Fri 10 August) 'day' => [D] [MNn] (e.g., 10 August) 'day-short' => [D] [MNn,*-3] (e.g., 10 Aug) 'day+year' => [D] [MNn], [Y] (e.g., 10 August, 2013) 'day-short+year' => [D] [MNn,*-3], [Y] (e.g., 10 Aug, 2013) 'ISO' => [Y0001]-[M01]-[D01] (e.g., 2012-08-10) date picture string => Any valid XSLT date picture specification (see http://www.w3.org/TR/xslt20/#date-picture-string). @offset: A signed offset specified as an XSLT duration, e.g., "P1D" for plus one day, "-P4D" for minus four days. --> <template name="TeachingPeriodDate" match="TeachingPeriodDate"> <common> <xsl:call-template name="format-teaching-date"> {$shared_parameters} <xsl:with-param name="format"> <xsl:choose> <!-- Named date formats. --> <xsl:when test="@format = 'day+long-name'"> <xsl:text>[FNn] [D] [MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'day+short-name'"> <xsl:text>[FNn,*-3] [D] [MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'day'"> <xsl:text>[D] [MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'day-short'"> <xsl:text>[D] [MNn,*-3]</xsl:text> </xsl:when> <xsl:when test="@format = 'day+year'"> <xsl:text>[D] [MNn], [Y]</xsl:text> </xsl:when> <xsl:when test="@format = 'day-short+year'"> <xsl:text>[D] [MNn,*-3], [Y]</xsl:text> </xsl:when> <xsl:when test="@format = 'ISO'"> <xsl:text>[Y0001]-[M01]-[D01]</xsl:text> </xsl:when> <xsl:otherwise> <xsl:value-of select="@format" /> </xsl:otherwise> </xsl:choose> <xsl:if test="not( @format )"> <xsl:text>[Y0001]-[M01]-[D01]</xsl:text> </xsl:if> </xsl:with-param> <xsl:with-param name="offset"> <xsl:value-of select="@offset" /> <xsl:if test="not( @offset )"> <xsl:text>P0D</xsl:text> </xsl:if> </xsl:with-param> </xsl:call-template> </common> </template> <!-- Generate a date range corresponding to a given teaching week in a teaching period (i.e., Monday to Friday). @period: The teaching period for which to generate the date. [required] @week: The number of the week in the specified teaching period. [required] @format: How to format the month name in the date range. 'long' => Output the full month name (e.g., August) 'short' => Output only the first three characters of the month name (e.g., Aug) @wrap: Whether to wrap the date range across two lines (usually for insertion into a narrow cell in a calendar table). 'true' [default] 'false' --> <template name="TeachingPeriodDateRange" match="TeachingPeriodDateRange"> <common> <xsl:call-template name="format-teaching-date-range"> {$shared_parameters} <xsl:with-param name="month-format"> <xsl:choose> <xsl:when test="@format = 'long'"> <xsl:text>[MNn]</xsl:text> </xsl:when> <xsl:when test="@format = 'short'"> <xsl:text>[MNn,*-3]</xsl:text> </xsl:when> <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Attribute "format" must be either "long" or "short", not "</xsl:text> <xsl:value-of select="@format" /> <xsl:text>".</xsl:text> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:with-param> <!-- Convert @wrap into a boolean value. --> <xsl:with-param name="wrap"> <xsl:value-of select="@wrap = 'yes'" /> </xsl:with-param> </xsl:call-template> </common> </template> EOT; // Generate the XSLT templates that do all the work. These essentially end up being huge parameterised lookup tables. generate_template( $first_monday, $periods, 'format-teaching-date' ); generate_template( $first_monday, $periods, 'format-teaching-date-range' ); // Footer boilerplate. print( "</stylesheet>\n" ); exit; //////////////////////////////////////////////////////////////////////////////// function generate_template( $first_monday, $periods, $template_name ) { // A couple of handy date intervals: four days to the end of the current week, // and seven days to the start of next week. $plus_four_days = new DateInterval( 'P4D' ); $plus_seven_days = new DateInterval( 'P7D' ); // Initial boilerplate for the template. switch ( $template_name ) { case 'format-teaching-date': print<<<EOT <!-- Generate a date (default Monday) for the specified teaching period and week (or break), using the specified date format, with an optional offset from the Monday of the week. Most of the body of this template is automatically generated by the script ../generate_calendar_dates.php. See the attributes of the TeachingPeriodDate template above for descriptions of the parameters. --> <template name="format-teaching-date"> <common> <xsl:param name="period" as="xs:string" /> <xsl:param name="week" as="xs:string" /> <xsl:param name="format" as="xs:string" /> <xsl:param name="offset" as="xs:dayTimeDuration" /> <xsl:choose> EOT; break; case 'format-teaching-date-range': print<<<EOT <!-- Generate a date range for the specified teaching period and week (or break), using the specified month format, with optional wrapping. Most of the body of this template is automatically generated by the script ../generate_calendar_dates.php. See the attributes of the TeachingPeriodDateRange template above for descriptions of the parameters. \$month-format is derived from @format. --> <template name="format-teaching-date-range"> <common> <xsl:param name="period" as="xs:string" /> <xsl:param name="week" as="xs:string" /> <xsl:param name="month-format" as="xs:string" /> <xsl:param name="wrap" as="xs:boolean" /> <xsl:choose> EOT; break; default: // WTF?!? print "Unrecognised template name \"$template_name\", terminating.\n"; exit; break; } // switch template name // Generate XSLT code for each teaching period. foreach ( $periods as $period_name => $period_data ) { print<<<EOT <xsl:when test="\$period = '{$period_name}'"> <xsl:choose> EOT; $week_start = clone( $first_monday ); // Jump forward the correct number of weeks to the start of the teaching period. $week_start->add( new DateInterval( sprintf( "P%dD", ( $period_data['first_week'] - 1 ) * 7 ) ) ); $week_number = 0; $num_breaks = 0; // Grab the first break week off the front of the list. sort( $period_data['break_weeks'] ); $break_week = array_shift( $period_data['break_weeks'] ); // Generate XSLT code for each teaching week and breaks within the period. for ( $week = $period_data['first_week']; $week <= $period_data['last_week']; $week++ ) { $week_end = clone( $week_start ); $week_end->add( $plus_four_days ); // Handle breaks. if ( ( $week >= $break_week['break_starts'] ) && ( $week <= $break_week['break_ends'] ) ) { if ( $week == $break_week['break_starts'] ) { $num_breaks++; // Work out the end date of the break. $break_end = clone( $week_start ); // Note: +4 days to get to Friday. $break_end->add( new DateInterval( sprintf( "P%dD", ( ( $break_week['break_ends'] - $break_week['break_starts'] ) * 7 ) + 4 ) ) ); switch ( $template_name ) { case 'format-teaching-date': generate_date( "B{$num_breaks}", $week_start ); break; case 'format-teaching-date-range': if ( $week_start->format( 'n' ) == $break_end->format( 'n' ) ) { generate_same_month_date_range( "B{$num_breaks}", $week_start, $break_end ); } // if break start and break end are in the same month else { generate_different_month_date_range( "B{$num_breaks}", $week_start, $break_end ); } // else break start and break end are in different months break; default: // WTF?!? print "Unrecognised template name \"$template_name\", terminating.\n"; exit; break; } // switch template name } // if current week == first week of break if ( $week == $break_week['break_ends'] ) { // Grab the next break week off the front of the list. $break_week = array_shift( $period_data['break_weeks'] ); } // if current week == last week of break } // if current week is a break week else { $week_number++; switch ( $template_name ) { case 'format-teaching-date': generate_date( $week_number, $week_start ); break; case 'format-teaching-date-range': if ( $week_start->format( 'n' ) == $week_end->format( 'n' ) ) { generate_same_month_date_range( $week_number, $week_start, $week_end ); } // if break start and break end are in the same month else { generate_different_month_date_range( $week_number, $week_start, $week_end ); } // else break start and break end are in different months break; default: // WTF?!? print "Unrecognised template name \"$template_name\", terminating.\n"; exit; break; } // switch template name } // else normal teaching week $week_start->add( $plus_seven_days ); } // for each week // Terminating boilerplate for this teaching period. print<<<EOT <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Invalid {$period_name} week specification "</xsl:text> <xsl:value-of select="\$week" /> <xsl:text>".</xsl:text> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:when> EOT; } // foreach teaching period // Terminating boilerplate for the template. print<<<EOT </xsl:choose> </common> </template> EOT; } // generate_template //////////////////////////////////////////////////////////////////////////////// function generate_date( $week_spec, $date ) { print<<<EOT <xsl:when test="\$week = '{$week_spec}'"> <xsl:value-of select="format-date( xs:date( '{$date->format( 'Y-m-d' )}' ) + \$offset, \$format )" /> </xsl:when> EOT; } // generate_same_month_date_range //////////////////////////////////////////////////////////////////////////////// function generate_same_month_date_range( $week_spec, $start_date, $end_date ) { print<<<EOT <xsl:when test="\$week = '{$week_spec}'"> <xsl:value-of select="format-date( xs:date( '{$start_date->format( 'Y-m-d' )}' ), '[D]' )" /> <xsl:call-template name="endash" /> <xsl:value-of select="format-date( xs:date( '{$end_date->format( 'Y-m-d' )}' ), '[D] ' )" /> <xsl:if test="\$wrap"> <xsl:call-template name="newline" /> </xsl:if> <xsl:value-of select="format-date( xs:date( '{$end_date->format( 'Y-m-d' )}' ), \$month-format )" /> </xsl:when> EOT; } // generate_same_month_date_range function generate_different_month_date_range( $week_spec, $start_date, $end_date ) { print<<<EOT <xsl:when test="\$week = '{$week_spec}'"> <xsl:value-of select="format-date( xs:date( '{$start_date->format( 'Y-m-d' )}' ), concat( '[D] ', \$month-format ) )" /> <xsl:if test="\$wrap"> <xsl:call-template name="newline" /> </xsl:if> <xsl:call-template name="endash" /> <xsl:value-of select="format-date( xs:date( '{$end_date->format( 'Y-m-d' )}' ), concat( '[D] ', \$month-format ) )" /> </xsl:when> EOT; } // generate_different_month_date_range ?>
Ignore Space
Show notes
View
xml2xslt.xsl
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsl-out="[irrelevant]" xmlns:exsl="http://exslt.org/common" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:infosci="http://info-nts-12.otago.ac.nz/infosci"> <!-- XSLT transformation for a master XML document defining how to transform elements in the course handbook source into HTML and LaTeX. Hmm, meta-stylesheet?! --> <!-- <xsl:output method="text" encoding="utf-8" media-type="text/xml" /> --> <xsl:output method="xml" encoding="utf-8" cdata-section-elements="html" /> <!-- What target format are we generating a styesheet for? Possible values: html, xhtml, latex (includes pdflatex), xelatex. --> <xsl:param name="target-format"><xsl:text>html</xsl:text></xsl:param> <!-- Define an alias for the xsl namespace to avoid confusion when generating xsl: elements in the output of this stylesheet. --> <xsl:namespace-alias stylesheet-prefix="xsl-out" result-prefix="xsl" /> <!-- Some useful variables. (Could some of these become callable templates?) --> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:variable name="space"><xsl:text> </xsl:text></xsl:variable> <xsl:template match="/"> <xsl-out:stylesheet version="2.0" exclude-result-prefixes="exsl"> <xsl-out:strip-space elements="*" /> <xsl-out:param name="subject-code"><xsl:text>INFO</xsl:text></xsl-out:param> <xsl-out:param name="paper-number" /> <xsl-out:param name="paper-year" select="year-from-date( current-date() )"/> <xsl-out:param name="period-code" /> <xsl-out:param name="showanswers"><xsl:text>no</xsl:text></xsl-out:param> <xsl-out:param name="base-path"><xsl:text>.</xsl:text></xsl-out:param> <!-- Full period string corresponding to the supplied period code. --> <xsl-out:variable name="period-string" select="infosci:expand-period-code( $period-code )" /> <!-- <xsl-out:choose> <xsl-out:when test="$period-code = 'SS'"> <xsl-out:text>Summer School</xsl-out:text> </xsl-out:when> <xsl-out:when test="$period-code = 'S1'"> <xsl-out:text>Semester One</xsl-out:text> </xsl-out:when> <xsl-out:when test="$period-code = 'S2'"> <xsl-out:text>Semester Two</xsl-out:text> </xsl-out:when> <xsl-out:when test="$period-code = 'FY'"> <xsl-out:text>Full Year</xsl-out:text> </xsl-out:when> <xsl-out:otherwise> <xsl-out:message terminate="yes"> <xsl-out:text>Unrecognised period code "</xsl-out:text> <xsl-out:value-of select="$period-code" /> <xsl-out:text>".</xsl-out:text> </xsl-out:message> </xsl-out:otherwise> </xsl-out:choose> </xsl-out:variable> --> <!-- The date and time when the document was last built. --> <xsl-out:variable name="date-built" select="current-dateTime()" /> <!-- Get the name of the document being processed. --> <xsl-out:variable name="document-name" select="reverse( tokenize( base-uri(), '/' ) )[1]" /> <!-- Include the generated Oracle documentation code. --> <xsl-out:include href="oracle-docs.xsl" /> <!-- Include the generated teaching calendar dates. --> <!-- <xsl-out:include href="calendar_dates.xsl" /> --> <!-- We're going to use the text encoding in a few different places, so let's work out what it should be now. --> <xsl:variable name="text-encoding"> <xsl:choose> <xsl:when test="($target-format = 'latex') or ($target-format = 'html')"> <xsl:text>iso-8859-1</xsl:text> </xsl:when> <xsl:when test="($target-format = 'xelatex') or ($target-format = 'xhtml')"> <xsl:text>utf-8</xsl:text> </xsl:when> <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Sorry, unknown target format: </xsl:text><xsl:value-of select="$target-format" /> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- root-level documents should say what type/class of document they are (lecture, tutorial, generic, ...). Do we want to call this a class or a type? --> <!-- <xsl:variable name="doctype"><xsl:value-of select="/document/@type" /></xsl:variable> --> <!-- First, output the document preamble according to target format. This is generally different for each target format. --> <xsl:choose> <!-- The HTML formats are fairly simple: the only things that change are the doctypes, version number and text encoding. --> <xsl:when test="$target-format = 'html'"> <xsl-out:output method="html" encoding="{$text-encoding}" version="4.01" media-type="text/html" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" doctype-system="http://www.w3.org/TR/html4/loose.dtd" /> </xsl:when> <xsl:when test="$target-format = 'xhtml'"> <xsl-out:output method="xml" encoding="{$text-encoding}" byte-order-mark="no" version="1.1" media-type="text/html" doctype-public="-//W3C//DTD XHTML 1.1//EN" doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" /> </xsl:when> <!-- The LaTeX formats, however have a bunch of miscellaneous boilerplate that appears at the start of every document, and requires more complex interleaving because of hyperref. Since we need to stuff this into a template later on, we define this using a callable template, so that we can do useful things like apply-templates within it (which wouldn't work if we just used a variable). --> <xsl:when test="$target-format = 'latex'"> <xsl-out:output method="text" encoding="{$text-encoding}" media-type="text/plain" /> <!-- Set to no if you want this to be included inside another document. Appears here because it's used in the document preamble. --> <xsl-out:param name="standalone"><xsl:text>yes</xsl:text></xsl-out:param> <xsl-out:template name="latex-preamble"> <xsl-out:text> \usepackage{mathpazo} % mathpple is deprecated \usepackage[T1]{fontenc} \usepackage{textcomp} </xsl-out:text> <xsl-out:apply-templates select="environment/latex-packages" /> <xsl-out:text> % Safer to specify the hyperref options directly rather than relying on % the default hyperref.cfg, as XeLaTeX seems to ignore it :(. \usepackage[ pdfpagemode=UseNone,% colorlinks,% urlcolor=blue,% citecolor=blue,% linkcolor=blue,% breaklinks ]{hyperref} \renewcommand{\ttdefault}{blg} </xsl-out:text> </xsl-out:template> </xsl:when> <xsl:when test="$target-format = 'xelatex'"> <xsl-out:output method="text" encoding="{$text-encoding}" media-type="text/plain" /> <!-- Set to no if you want this to be included inside another document. Appears here because it's used in the document preamble. --> <xsl-out:param name="standalone"><xsl:text>yes</xsl:text></xsl-out:param> <xsl-out:template name="latex-preamble"> <xsl-out:text> \usepackage[no-math]{fontspec} \usepackage{mathspec} \usepackage{xunicode} \usepackage{xltxtra} \usepackage{textcomp} % ??? </xsl-out:text> <xsl-out:apply-templates select="environment/latex-packages" /> <xsl-out:text> % Safer to specify the hyperref options directly rather than relying on % the default hyperref.cfg, as XeLaTeX seems to ignore it :(. \usepackage[ pdfpagemode=UseNone,% colorlinks,% urlcolor=blue,% citecolor=blue,% linkcolor=blue,% breaklinks ]{hyperref} \defaultfontfeatures{Mapping=tex-text} \setmainfont{TeX Gyre Pagella} \setmathsfont(Digits){TeX Gyre Pagella} \setmonofont[Scale=MatchLowercase]{Letter Gothic 12 Pitch} </xsl-out:text> </xsl-out:template> </xsl:when> <!-- No need for an otherwise, as weird target formats will have already been trapped by the definition of the text-encoding variable above. --> </xsl:choose> <!-- Next, output the main document body according to target format. This is generally the same across similar target formats. --> <xsl:choose> <xsl:when test="($target-format = 'html') or ($target-format = 'xhtml')"> <!-- *** (X)HTML Output *** --> <!-- Old version: before using xsl:namespace-alias: --> <!-- <xsl-out:element name="xsl:output"> <xsl-out:attribute name="method">html</xsl-out:attribute> <xsl-out:attribute name="encoding">UTF-8</xsl-out:attribute> <xsl-out:attribute name="media-type">text/html</xsl-out:attribute> <xsl-out:attribute name="doctype-public">-//W3C//DTD HTML 4.01 Transitional//EN</xsl-out:attribute> </xsl-out:element> --> <!-- Default to PNG images for web dispay. --> <xsl-out:param name="image-format"><xsl:text>png</xsl:text></xsl-out:param> <!-- Nope, includes can only appear as a child of xsl:stylesheet. --> <!-- <xsl-out:include href="xml2html-root.xsl" /> --> <xsl-out:template match="/document"> <xsl-out:comment> THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! </xsl-out:comment> <html> <head> <xsl-out:element name="link"> <xsl-out:attribute name="rel"> <xsl-out:text>Stylesheet</xsl-out:text> </xsl-out:attribute> <xsl-out:attribute name="href"> <xsl-out:text>https://blackboard.otago.ac.nz/bbcswebdav/courses/</xsl-out:text> <xsl-out:value-of select="$subject-code" /> <xsl-out:value-of select="$paper-number" /> <xsl-out:text>_</xsl-out:text> <xsl-out:value-of select="$period-code" /> <xsl-out:text>DNI_</xsl-out:text> <xsl-out:value-of select="$paper-year" /> <xsl-out:text>/db_styles.css</xsl-out:text> </xsl-out:attribute> <xsl-out:attribute name="type"> <xsl-out:text>text/css</xsl-out:text> </xsl-out:attribute> </xsl-out:element> <xsl-out:element name="meta"> <xsl-out:attribute name="http-equiv"> <xsl-out:text>Content-type</xsl-out:text> </xsl-out:attribute> <xsl-out:attribute name="content"> <xsl-out:text> <xsl:text>text/html;charset=</xsl:text> <xsl:value-of select="$text-encoding" /> </xsl-out:text> </xsl-out:attribute> </xsl-out:element> <title> <xsl-out:choose> <xsl-out:when test="@class = 'calendar'"> <xsl-out:value-of select="$subject-code" /> <xsl-out:value-of select="$paper-number" /> <xsl-out:text> Teaching Calendar, </xsl-out:text> <xsl-out:value-of select="$period-string" /> <xsl-out:text>, </xsl-out:text> <xsl-out:value-of select="$paper-year" /> </xsl-out:when> <xsl-out:otherwise> <xsl-out:apply-templates select="title" mode="preamble" /> </xsl-out:otherwise> </xsl-out:choose> </title> </head> <body> <xsl-out:choose> <xsl-out:when test="@class = 'calendar'" /> <xsl-out:otherwise> <xsl-out:apply-templates select="title" mode="title" /> <xsl-out:apply-templates select="due-date" mode="title" /> </xsl-out:otherwise> </xsl-out:choose> <xsl-out:apply-templates /> <!-- How best to approach this - certain elements that need special handling (e.g. title, author) shouldn't be passed through here as well. --> <!-- How about we just match certain elements that we know can be handled safely, e.g. sections, paragraphs, ...? (...and their aliases?) --> <!-- Are introductions just another section, or should there be an introduction element? --> <!-- The solution is to get cleverer with our templates, e.g., multiple templates for <title> that have different match patterns (document/title, section/title). --> <!-- We also need to define an empty template for the <document-metadata> element so that its contents get ignored. --> <!-- Once these are done, we can just go apply-templates and forget about it. --> <hr /> <!-- Since HTML doesn't support footnotes as such, we instead include them as endnotes at the end of the document. --> <xsl-out:if test="count(//footnote) > 0"> <h3>Notes</h3> <xsl-out:apply-templates select="//footnote" mode="list" /> <hr /> </xsl-out:if> <xsl-out:call-template name="build-date-internal"> <xsl-out:with-param name="format">long</xsl-out:with-param> <xsl-out:with-param name="style">footer</xsl-out:with-param> </xsl-out:call-template> </body> </html> </xsl-out:template> </xsl:when> <xsl:when test="($target-format = 'latex') or ($target-format = 'xelatex')"> <!-- Set to pdf if using PDFLaTeX, otherwise eps. --> <xsl-out:param name="image-format"><xsl:text>pdf</xsl:text></xsl-out:param> <!-- Generate macro to encode the document class name. This is currently BROKEN. If the document class specified loads packages that are then loaded by the boilerplate below with different optons, it will cause LaTeX "option clash" errors. Therefore, DO NOT use the @latex-document-class attribute for now until a solution can be devised! --> <xsl-out:template name="setup-document-class"> <xsl-out:text> \def\DocumentClass{</xsl-out:text> <xsl-out:value-of select="@latex-document-class" /> <xsl-out:if test="not( @latex-document-class )"> <xsl-out:text>article</xsl-out:text> </xsl-out:if> <xsl-out:text>} </xsl-out:text> </xsl-out:template> <!-- *** LaTeX Source Output *** --> <!-- Should this produce a LaTeX source fragment or an entire valid source document? --> <xsl-out:template match="/document"> <xsl-out:choose> <xsl-out:when test="$standalone = 'yes'"> <xsl-out:text> % THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! </xsl-out:text> <xsl-out:call-template name="setup-document-class" /> <xsl-out:text> \PassOptionsToClass{\LaTeXOptions}{\DocumentClass} \documentclass{\DocumentClass} \usepackage[margin=1in]{geometry} \usepackage{multirow} \usepackage{graphicx} \usepackage{verbatim} % needed for \verbatiminput \usepackage{relalg} % needed for join operators \usepackage{pifont} \usepackage{siunitx} % number and SI unit formatting \usepackage{listings} % nicely formatted code listings \usepackage[normalem]{ulem} % fancy underlining, strikeout, etc. \usepackage{boxedminipage} \usepackage{parskip} % Space-separated rather than indented paragraphs. \usepackage{rotating} % Rotate stuff. </xsl-out:text> <xsl-out:call-template name="latex-preamble" /> <xsl-out:apply-templates select="environment/latex-commands" /> <xsl-out:text> \newenvironment{answer}{\par\vspace{0.5em}\itshape}{\normalfont\vspace{1.5em}} % Listings setup. We preload the most obviously like languages to speed things % up. Other languages will still work, just not quite as quickly. \lstloadlanguages{Oracle} \lstset{basicstyle=\ttfamily,basewidth=0.5em,escapeinside={(@}{@)}, showspaces=false,showstringspaces=false} % Environment for worksheet exercises. \newcounter{exercise} \setcounter{exercise}{0} \newenvironment{exercise}% {\noindent\refstepcounter{exercise}\begin{boxedminipage}{\columnwidth}\textbf{Exercise \theexercise: }}% {\end{boxedminipage}} </xsl-out:text> <xsl-out:apply-templates select="title" mode="preamble" /> <xsl-out:call-template name="newline-internal" /> <xsl-out:apply-templates select="author" mode="preamble" /> <xsl-out:call-template name="newline-internal" /> <xsl-out:apply-templates select="date|due-date" mode="preamble" /> <xsl-out:call-template name="newline-internal" /> <xsl-out:text> \begin{document} </xsl-out:text> <xsl-out:if test="not( @suppress-latex-title ) or ( @suppress-latex-title != 'yes' )"> <xsl-out:text> \maketitle </xsl-out:text> </xsl-out:if> <xsl-out:text> </xsl-out:text> <xsl-out:apply-templates /> <!-- If you're having problems with the build date appearing in weird or annoying locations (usually because of floating items like tables and figures), set document/@auto-latex-build-date to "no". You can then use the build-date element to insert the build date wherever you like, if necessary. This really only applies to LaTeX documents. The behaviour of HTML documents is much more predictable because they don't have elements with "minds of their own", so the build date is guaranteed to always appear at the very end. --> <xsl-out:if test="not( @auto-latex-build-date ) or ( @auto-latex-build-date != 'no' )"> <xsl-out:call-template name="build-date-internal"> <xsl-out:with-param name="format">long</xsl-out:with-param> <xsl-out:with-param name="style">footer</xsl-out:with-param> </xsl-out:call-template> </xsl-out:if> <xsl-out:text>\end{document}</xsl-out:text> </xsl-out:when> <!-- Not standalone: --> <xsl-out:otherwise> <xsl-out:apply-templates select="title" mode="chapter" /> <xsl-out:apply-templates select="*[not(self::title)]" /> <xsl-out:if test="not( @auto-latex-build-date ) or ( @auto-latex-build-date != 'no' )"> <xsl-out:call-template name="build-date-internal"> <xsl-out:with-param name="format">long</xsl-out:with-param> <xsl-out:with-param name="style">footer</xsl-out:with-param> </xsl-out:call-template> </xsl-out:if> </xsl-out:otherwise> </xsl-out:choose> </xsl-out:template> </xsl:when> <!-- No need for an otherwise, as weird formats will have already been trapped by the definition of the text-encoding variable above. --> </xsl:choose> <xsl:apply-templates /> </xsl-out:stylesheet> </xsl:template> <!-- Copy across templates according to the target format. If there's no common code for a particular format, an empty template is generated. --> <xsl:template match="template"> <xsl-out:template> <!-- Much easier to just copy all attributes across verbatim rather than copying specific named attributes, because we might want to use attributes that weren't originally anticipated. Might this be a problem in future? --> <xsl:copy-of select="@*" /> <!-- Copy across code that is common to ALL target formats. Any code not specific to a particular target format will therefore always appear FIRST in the resulting template. --> <xsl:copy-of select="common[not(@formats)]/node()" /> <!-- Copy across code that is specific to the current format. --> <xsl:copy-of select="common[contains(@formats, concat('/', $target-format, '/'))]/node()" /> <xsl:copy-of select="*[name(.) = $target-format]/node()" /> </xsl-out:template> </xsl:template> <!-- Dealing with functions is slightly more complex than templates, as functions aren't allowed to be empty. We therefore have to completely ignore function definitions that have no code for the target format. The second, more specific template will match in preference to the empty one. --> <xsl:template match="function" /> <xsl:template match="function[common[contains( @formats, concat( '/', $target-format, '/' ) )]]|function[common[not( @formats )]]"> <xsl-out:function> <!-- Much easier to just copy all attributes across verbatim rather than copying specific named attributes, because we might want to use attributes that weren't originally anticipated. Might this be a problem in future? --> <xsl:copy-of select="@*" /> <!-- Copy across code that is common to ALL target formats. Any code not specific to a particular target format will therefore always appear FIRST in the resulting template. --> <xsl:copy-of select="common[not( @formats )]/node()" /> <!-- Copy across code that is specific to the current format. --> <xsl:copy-of select="common[contains( @formats, concat( '/', $target-format, '/' ) )]/node()" /> <xsl:copy-of select="*[name( . )=$target-format]/node()" /> </xsl-out:function> </xsl:template> <!-- Include templates from a stylesheet sub-module. This enables us to modularise the master stylesheet. --> <xsl:template match="include"> <xsl:apply-templates select="document( @href )/stylesheet/*" /> </xsl:template> <!-- This template produces a template that calls another template, i.e. it implements a template alias. The generated template probably doesn't need a name, but we'll put one in anyway. --> <xsl:template match="alias"> <xsl-out:template> <xsl:attribute name="name"><xsl:value-of select="@source" /></xsl:attribute> <xsl:attribute name="match"><xsl:value-of select="@source" /></xsl:attribute> <xsl-out:call-template> <xsl:attribute name="name"><xsl:value-of select="@target" /></xsl:attribute> </xsl-out:call-template> </xsl-out:template> </xsl:template> <!-- This template produces a template for processing style-oriented markup, such as empahsis, foreign terms, and quotations. --> <!-- <xsl:template match="d"> </xsl-out:template match> --> <!-- This template produces a template for processing an element that refers to a hyperlink. --> <xsl:template match="hyperlink"> </xsl:template> </xsl:stylesheet>
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsl-out="[irrelevant]" xmlns:exsl="http://exslt.org/common" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:infosci="http://info-nts-12.otago.ac.nz/infosci"> <!-- XSLT transformation for a master XML document defining how to transform elements in the course handbook source into HTML and LaTeX. Hmm, meta-stylesheet?! --> <!-- <xsl:output method="text" encoding="utf-8" media-type="text/xml" /> --> <xsl:output method="xml" encoding="utf-8" cdata-section-elements="html" /> <!-- What target format are we generating a styesheet for? Possible values: html, xhtml, latex (includes pdflatex), xelatex. --> <xsl:param name="target-format"><xsl:text>html</xsl:text></xsl:param> <!-- Define an alias for the xsl namespace to avoid confusion when generating xsl: elements in the output of this stylesheet. --> <xsl:namespace-alias stylesheet-prefix="xsl-out" result-prefix="xsl" /> <!-- Some useful variables. (Could some of these become callable templates?) --> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:variable name="space"><xsl:text> </xsl:text></xsl:variable> <xsl:template match="/"> <xsl-out:stylesheet version="2.0" exclude-result-prefixes="exsl"> <xsl-out:strip-space elements="*" /> <xsl-out:param name="subject-code"><xsl:text>INFO</xsl:text></xsl-out:param> <xsl-out:param name="paper-number" /> <xsl-out:param name="paper-year" select="year-from-date( current-date() )"/> <xsl-out:param name="period-code" /> <xsl-out:param name="showanswers"><xsl:text>no</xsl:text></xsl-out:param> <xsl-out:param name="base-path"><xsl:text>.</xsl:text></xsl-out:param> <!-- Full period string corresponding to the supplied period code. --> <xsl-out:variable name="period-string" select="infosci:expand-period-code( $period-code )" /> <!-- <xsl-out:choose> <xsl-out:when test="$period-code = 'SS'"> <xsl-out:text>Summer School</xsl-out:text> </xsl-out:when> <xsl-out:when test="$period-code = 'S1'"> <xsl-out:text>Semester One</xsl-out:text> </xsl-out:when> <xsl-out:when test="$period-code = 'S2'"> <xsl-out:text>Semester Two</xsl-out:text> </xsl-out:when> <xsl-out:when test="$period-code = 'FY'"> <xsl-out:text>Full Year</xsl-out:text> </xsl-out:when> <xsl-out:otherwise> <xsl-out:message terminate="yes"> <xsl-out:text>Unrecognised period code "</xsl-out:text> <xsl-out:value-of select="$period-code" /> <xsl-out:text>".</xsl-out:text> </xsl-out:message> </xsl-out:otherwise> </xsl-out:choose> </xsl-out:variable> --> <!-- The date and time when the document was last built. --> <xsl-out:variable name="date-built" select="current-dateTime()" /> <!-- Get the name of the document being processed. --> <xsl-out:variable name="document-name" select="reverse( tokenize( base-uri(), '/' ) )[1]" /> <!-- Include the generated Oracle documentation code. --> <xsl-out:include href="oracle-docs.xsl" /> <!-- Include the generated teaching calendar dates. --> <!-- <xsl-out:include href="calendar_dates.xsl" /> --> <!-- We're going to use the text encoding in a few different places, so let's work out what it should be now. --> <xsl:variable name="text-encoding"> <xsl:choose> <xsl:when test="($target-format = 'latex') or ($target-format = 'html')"> <xsl:text>iso-8859-1</xsl:text> </xsl:when> <xsl:when test="($target-format = 'xelatex') or ($target-format = 'xhtml')"> <xsl:text>utf-8</xsl:text> </xsl:when> <xsl:otherwise> <xsl:message terminate="yes"> <xsl:text>Sorry, unknown target format: </xsl:text><xsl:value-of select="$target-format" /> </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- root-level documents should say what type/class of document they are (lecture, tutorial, generic, ...). Do we want to call this a class or a type? --> <!-- <xsl:variable name="doctype"><xsl:value-of select="/document/@type" /></xsl:variable> --> <!-- First, output the document preamble according to target format. This is generally different for each target format. --> <xsl:choose> <!-- The HTML formats are fairly simple: the only things that change are the doctypes, version number and text encoding. --> <xsl:when test="$target-format = 'html'"> <xsl-out:output method="html" encoding="{$text-encoding}" version="4.01" media-type="text/html" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" doctype-system="http://www.w3.org/TR/html4/loose.dtd" /> </xsl:when> <xsl:when test="$target-format = 'xhtml'"> <xsl-out:output method="xml" encoding="{$text-encoding}" byte-order-mark="no" version="1.1" media-type="text/html" doctype-public="-//W3C//DTD XHTML 1.1//EN" doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" /> </xsl:when> <!-- The LaTeX formats, however have a bunch of miscellaneous boilerplate that appears at the start of every document, and requires more complex interleaving because of hyperref. Since we need to stuff this into a template later on, we define this using a callable template, so that we can do useful things like apply-templates within it (which wouldn't work if we just used a variable). --> <xsl:when test="$target-format = 'latex'"> <xsl-out:output method="text" encoding="{$text-encoding}" media-type="text/plain" /> <!-- Set to no if you want this to be included inside another document. Appears here because it's used in the document preamble. --> <xsl-out:param name="standalone"><xsl:text>yes</xsl:text></xsl-out:param> <xsl-out:template name="latex-preamble"> <xsl-out:text> \usepackage{mathpazo} % mathpple is deprecated \usepackage[T1]{fontenc} \usepackage{textcomp} </xsl-out:text> <xsl-out:apply-templates select="environment/latex-packages" /> <xsl-out:text> % Safer to specify the hyperref options directly rather than relying on % the default hyperref.cfg, as XeLaTeX seems to ignore it :(. \usepackage[ pdfpagemode=UseNone,% colorlinks,% urlcolor=blue,% citecolor=blue,% linkcolor=blue,% breaklinks ]{hyperref} \renewcommand{\ttdefault}{blg} </xsl-out:text> </xsl-out:template> </xsl:when> <xsl:when test="$target-format = 'xelatex'"> <xsl-out:output method="text" encoding="{$text-encoding}" media-type="text/plain" /> <!-- Set to no if you want this to be included inside another document. Appears here because it's used in the document preamble. --> <xsl-out:param name="standalone"><xsl:text>yes</xsl:text></xsl-out:param> <xsl-out:template name="latex-preamble"> <xsl-out:text> \usepackage[no-math]{fontspec} \usepackage{mathspec} \usepackage{xunicode} \usepackage{xltxtra} \usepackage{textcomp} % ??? </xsl-out:text> <xsl-out:apply-templates select="environment/latex-packages" /> <xsl-out:text> % Safer to specify the hyperref options directly rather than relying on % the default hyperref.cfg, as XeLaTeX seems to ignore it :(. \usepackage[ pdfpagemode=UseNone,% colorlinks,% urlcolor=blue,% citecolor=blue,% linkcolor=blue,% breaklinks ]{hyperref} \defaultfontfeatures{Mapping=tex-text} \setmainfont{TeX Gyre Pagella} \setmathsfont(Digits){TeX Gyre Pagella} \setmonofont[Scale=MatchLowercase]{Letter Gothic 12 Pitch} </xsl-out:text> </xsl-out:template> </xsl:when> <!-- No need for an otherwise, as weird target formats will have already been trapped by the definition of the text-encoding variable above. --> </xsl:choose> <!-- Next, output the main document body according to target format. This is generally the same across similar target formats. --> <xsl:choose> <xsl:when test="($target-format = 'html') or ($target-format = 'xhtml')"> <!-- *** (X)HTML Output *** --> <!-- Old version: before using xsl:namespace-alias: --> <!-- <xsl-out:element name="xsl:output"> <xsl-out:attribute name="method">html</xsl-out:attribute> <xsl-out:attribute name="encoding">UTF-8</xsl-out:attribute> <xsl-out:attribute name="media-type">text/html</xsl-out:attribute> <xsl-out:attribute name="doctype-public">-//W3C//DTD HTML 4.01 Transitional//EN</xsl-out:attribute> </xsl-out:element> --> <!-- Default to PNG images for web dispay. --> <xsl-out:param name="image-format"><xsl:text>png</xsl:text></xsl-out:param> <!-- Nope, includes can only appear as a child of xsl:stylesheet. --> <!-- <xsl-out:include href="xml2html-root.xsl" /> --> <xsl-out:template match="/document"> <xsl-out:comment> THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! </xsl-out:comment> <html> <head> <xsl-out:element name="link"> <xsl-out:attribute name="rel"> <xsl-out:text>Stylesheet</xsl-out:text> </xsl-out:attribute> <xsl-out:attribute name="href"> <xsl-out:text>https://blackboard.otago.ac.nz/bbcswebdav/courses/</xsl-out:text> <xsl-out:value-of select="$subject-code" /> <xsl-out:value-of select="$paper-number" /> <xsl-out:text>_</xsl-out:text> <xsl-out:value-of select="$period-code" /> <xsl-out:text>DNS_</xsl-out:text> <xsl-out:value-of select="$paper-year" /> <xsl-out:text>/db_styles.css</xsl-out:text> </xsl-out:attribute> <xsl-out:attribute name="type"> <xsl-out:text>text/css</xsl-out:text> </xsl-out:attribute> </xsl-out:element> <xsl-out:element name="meta"> <xsl-out:attribute name="http-equiv"> <xsl-out:text>Content-type</xsl-out:text> </xsl-out:attribute> <xsl-out:attribute name="content"> <xsl-out:text> <xsl:text>text/html;charset=</xsl:text> <xsl:value-of select="$text-encoding" /> </xsl-out:text> </xsl-out:attribute> </xsl-out:element> <title> <xsl-out:choose> <xsl-out:when test="@class = 'calendar'"> <xsl-out:value-of select="$subject-code" /> <xsl-out:value-of select="$paper-number" /> <xsl-out:text> Teaching Calendar, </xsl-out:text> <xsl-out:value-of select="$period-string" /> <xsl-out:text>, </xsl-out:text> <xsl-out:value-of select="$paper-year" /> </xsl-out:when> <xsl-out:otherwise> <xsl-out:apply-templates select="title" mode="preamble" /> </xsl-out:otherwise> </xsl-out:choose> </title> </head> <body> <xsl-out:choose> <xsl-out:when test="@class = 'calendar'" /> <xsl-out:otherwise> <xsl-out:apply-templates select="title" mode="title" /> <xsl-out:apply-templates select="due-date" mode="title" /> </xsl-out:otherwise> </xsl-out:choose> <xsl-out:apply-templates /> <!-- How best to approach this - certain elements that need special handling (e.g. title, author) shouldn't be passed through here as well. --> <!-- How about we just match certain elements that we know can be handled safely, e.g. sections, paragraphs, ...? (...and their aliases?) --> <!-- Are introductions just another section, or should there be an introduction element? --> <!-- The solution is to get cleverer with our templates, e.g., multiple templates for <title> that have different match patterns (document/title, section/title). --> <!-- We also need to define an empty template for the <document-metadata> element so that its contents get ignored. --> <!-- Once these are done, we can just go apply-templates and forget about it. --> <hr /> <!-- Since HTML doesn't support footnotes as such, we instead include them as endnotes at the end of the document. --> <xsl-out:if test="count(//footnote) > 0"> <h3>Notes</h3> <xsl-out:apply-templates select="//footnote" mode="list" /> <hr /> </xsl-out:if> <xsl-out:call-template name="build-date-internal"> <xsl-out:with-param name="format">long</xsl-out:with-param> <xsl-out:with-param name="style">footer</xsl-out:with-param> </xsl-out:call-template> </body> </html> </xsl-out:template> </xsl:when> <xsl:when test="($target-format = 'latex') or ($target-format = 'xelatex')"> <!-- Set to pdf if using PDFLaTeX, otherwise eps. --> <xsl-out:param name="image-format"><xsl:text>pdf</xsl:text></xsl-out:param> <!-- Generate macro to encode the document class name. This is currently BROKEN. If the document class specified loads packages that are then loaded by the boilerplate below with different optons, it will cause LaTeX "option clash" errors. Therefore, DO NOT use the @latex-document-class attribute for now until a solution can be devised! --> <xsl-out:template name="setup-document-class"> <xsl-out:text> \def\DocumentClass{</xsl-out:text> <xsl-out:value-of select="@latex-document-class" /> <xsl-out:if test="not( @latex-document-class )"> <xsl-out:text>article</xsl-out:text> </xsl-out:if> <xsl-out:text>} </xsl-out:text> </xsl-out:template> <!-- *** LaTeX Source Output *** --> <!-- Should this produce a LaTeX source fragment or an entire valid source document? --> <xsl-out:template match="/document"> <xsl-out:choose> <xsl-out:when test="$standalone = 'yes'"> <xsl-out:text> % THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! </xsl-out:text> <xsl-out:call-template name="setup-document-class" /> <xsl-out:text> \PassOptionsToClass{\LaTeXOptions}{\DocumentClass} \documentclass{\DocumentClass} \usepackage[margin=1in]{geometry} \usepackage{multirow} \usepackage{graphicx} \usepackage{verbatim} % needed for \verbatiminput \usepackage{relalg} % needed for join operators \usepackage{pifont} \usepackage{siunitx} % number and SI unit formatting \usepackage{listings} % nicely formatted code listings \usepackage[normalem]{ulem} % fancy underlining, strikeout, etc. \usepackage{boxedminipage} \usepackage{parskip} % Space-separated rather than indented paragraphs. \usepackage{rotating} % Rotate stuff. </xsl-out:text> <xsl-out:call-template name="latex-preamble" /> <xsl-out:apply-templates select="environment/latex-commands" /> <xsl-out:text> \newenvironment{answer}{\par\vspace{0.5em}\itshape}{\normalfont\vspace{1.5em}} % Listings setup. We preload the most obviously like languages to speed things % up. Other languages will still work, just not quite as quickly. \lstloadlanguages{Oracle} \lstset{basicstyle=\ttfamily,basewidth=0.5em,escapeinside={(@}{@)}, showspaces=false,showstringspaces=false} % Environment for worksheet exercises. \newcounter{exercise} \setcounter{exercise}{0} \newenvironment{exercise}% {\noindent\refstepcounter{exercise}\begin{boxedminipage}{\columnwidth}\textbf{Exercise \theexercise: }}% {\end{boxedminipage}} </xsl-out:text> <xsl-out:apply-templates select="title" mode="preamble" /> <xsl-out:call-template name="newline-internal" /> <xsl-out:apply-templates select="author" mode="preamble" /> <xsl-out:call-template name="newline-internal" /> <xsl-out:apply-templates select="date|due-date" mode="preamble" /> <xsl-out:call-template name="newline-internal" /> <xsl-out:text> \begin{document} </xsl-out:text> <xsl-out:if test="not( @suppress-latex-title ) or ( @suppress-latex-title != 'yes' )"> <xsl-out:text> \maketitle </xsl-out:text> </xsl-out:if> <xsl-out:text> </xsl-out:text> <xsl-out:apply-templates /> <!-- If you're having problems with the build date appearing in weird or annoying locations (usually because of floating items like tables and figures), set document/@auto-latex-build-date to "no". You can then use the build-date element to insert the build date wherever you like, if necessary. This really only applies to LaTeX documents. The behaviour of HTML documents is much more predictable because they don't have elements with "minds of their own", so the build date is guaranteed to always appear at the very end. --> <xsl-out:if test="not( @auto-latex-build-date ) or ( @auto-latex-build-date != 'no' )"> <xsl-out:call-template name="build-date-internal"> <xsl-out:with-param name="format">long</xsl-out:with-param> <xsl-out:with-param name="style">footer</xsl-out:with-param> </xsl-out:call-template> </xsl-out:if> <xsl-out:text>\end{document}</xsl-out:text> </xsl-out:when> <!-- Not standalone: --> <xsl-out:otherwise> <xsl-out:apply-templates select="title" mode="chapter" /> <xsl-out:apply-templates select="*[not(self::title)]" /> <xsl-out:if test="not( @auto-latex-build-date ) or ( @auto-latex-build-date != 'no' )"> <xsl-out:call-template name="build-date-internal"> <xsl-out:with-param name="format">long</xsl-out:with-param> <xsl-out:with-param name="style">footer</xsl-out:with-param> </xsl-out:call-template> </xsl-out:if> </xsl-out:otherwise> </xsl-out:choose> </xsl-out:template> </xsl:when> <!-- No need for an otherwise, as weird formats will have already been trapped by the definition of the text-encoding variable above. --> </xsl:choose> <xsl:apply-templates /> </xsl-out:stylesheet> </xsl:template> <!-- Copy across templates according to the target format. If there's no common code for a particular format, an empty template is generated. --> <xsl:template match="template"> <xsl-out:template> <!-- Much easier to just copy all attributes across verbatim rather than copying specific named attributes, because we might want to use attributes that weren't originally anticipated. Might this be a problem in future? --> <xsl:copy-of select="@*" /> <!-- Copy across code that is common to ALL target formats. Any code not specific to a particular target format will therefore always appear FIRST in the resulting template. --> <xsl:copy-of select="common[not(@formats)]/node()" /> <!-- Copy across code that is specific to the current format. --> <xsl:copy-of select="common[contains(@formats, concat('/', $target-format, '/'))]/node()" /> <xsl:copy-of select="*[name(.) = $target-format]/node()" /> </xsl-out:template> </xsl:template> <!-- Dealing with functions is slightly more complex than templates, as functions aren't allowed to be empty. We therefore have to completely ignore function definitions that have no code for the target format. The second, more specific template will match in preference to the empty one. --> <xsl:template match="function" /> <xsl:template match="function[common[contains( @formats, concat( '/', $target-format, '/' ) )]]|function[common[not( @formats )]]"> <xsl-out:function> <!-- Much easier to just copy all attributes across verbatim rather than copying specific named attributes, because we might want to use attributes that weren't originally anticipated. Might this be a problem in future? --> <xsl:copy-of select="@*" /> <!-- Copy across code that is common to ALL target formats. Any code not specific to a particular target format will therefore always appear FIRST in the resulting template. --> <xsl:copy-of select="common[not( @formats )]/node()" /> <!-- Copy across code that is specific to the current format. --> <xsl:copy-of select="common[contains( @formats, concat( '/', $target-format, '/' ) )]/node()" /> <xsl:copy-of select="*[name( . )=$target-format]/node()" /> </xsl-out:function> </xsl:template> <!-- Include templates from a stylesheet sub-module. This enables us to modularise the master stylesheet. --> <xsl:template match="include"> <xsl:apply-templates select="document( @href )/stylesheet/*" /> </xsl:template> <!-- This template produces a template that calls another template, i.e. it implements a template alias. The generated template probably doesn't need a name, but we'll put one in anyway. --> <xsl:template match="alias"> <xsl-out:template> <xsl:attribute name="name"><xsl:value-of select="@source" /></xsl:attribute> <xsl:attribute name="match"><xsl:value-of select="@source" /></xsl:attribute> <xsl-out:call-template> <xsl:attribute name="name"><xsl:value-of select="@target" /></xsl:attribute> </xsl-out:call-template> </xsl-out:template> </xsl:template> <!-- This template produces a template for processing style-oriented markup, such as empahsis, foreign terms, and quotations. --> <!-- <xsl:template match="d"> </xsl-out:template match> --> <!-- This template produces a template for processing an element that refers to a hyperlink. --> <xsl:template match="hyperlink"> </xsl:template> </xsl:stylesheet>
Show line notes below