Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old May 22nd, 2013, 08:16 AM
Authorized User
 
Join Date: May 2009
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
Default XSLT 1 transformation help

Hi all,
I am kind of stuck in implementing a rather tricky output for my given xml source.

My input is:

Code:
<PickUpOpeningHours>08:00 - 22:00</PickUpOpeningHours>
And I want an output similar to:

Code:
<SubSection SubTitle="OfficeHours">
	<Paragraph Name="Monday">
		<Text>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCCCCCCCC</Text>
	</Paragraph>
	<Paragraph Name="Tuesday">
		<Text>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCCCCCCCC</Text>
	</Paragraph>
	<Paragraph Name="Wednesday">
		<Text>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCCCCCCCC</Text>
	</Paragraph>
	<Paragraph Name="Thursday">
		<Text>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCCCCCCCC</Text>
	</Paragraph>
	<Paragraph Name="Friday">
		<Text>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCCCCCCCC</Text>
	</Paragraph>
	<Paragraph Name="Saturday">
		<Text>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCCCCCCCC</Text>
	</Paragraph>
	<Paragraph Name="Sunday">
		<Text>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCCCCCCCC</Text>
	</Paragraph>
</SubSection>
Let me explain the output logic.

C: closes
O: opened

Time is represented in 24 hours.

1 hour = 4 x 15 min (4 quarters)
24 hours = 96 x 15 min (96 quarters)

Total characters (C or O) will be 96 in total, where each character represents single quarter of time.

The input xml could be of two different formats:

Code:
<PickUpOpeningHours>07:00 - 23:00</PickUpOpeningHours>
OR

Code:
<PickUpOpeningHours>08:30 - 12:00, 14:30 - 18:00</PickUpOpeningHours>

Can someone please help me out in devising some solution for the above problem using xslt 1?

Thanks.
 
Old May 22nd, 2013, 09:09 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

Does it really have to be 1.0?

First pick out the opening and closing times using substring-before and substring-after. Translate 08:00 to an opening number $O=32 (= 8*4 + 0*1), and 22:00 to $C=88 similarly. Then define a couple of strings which are sufficiently long sequences of letter O and letter C respectively, select subsequences of these using substring(X, 1, $O), substring(X, $O, ($C - $O)), and substring(X, $C), and concatenate the results.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old May 22nd, 2013, 09:29 AM
Authorized User
 
Join Date: May 2009
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Thanks for replying. I have implemented it in xslt 2, but it is getting complicated and difficult to maintain :(

Here it is so far how it looks like:

Code:
<xsl:transform xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" exclude-result-prefixes=" xs xsl">
	<xsl:import href="mapp2flags.xsl"/>
	<xsl:output method="xml" version="1.0"/>
	<xsl:param name="day"/>
	<xsl:param name="closed24" select="'00:00 - 00:00'"/>
	<xsl:param name="open24" select="'00:00 - 24:00'"/>
	<xsl:variable name="weekDays">Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday,</xsl:variable>
	<xsl:variable name="delimiters" select="'&#x7c;'"/>
	<!-- xsl:template match="/">
		<xsl:apply-templates select="OPENING_HOURS"/>
	</xsl:template> -->

	
	<xsl:template match="PickUpOpeningHours|DropOffOpeningHours">
		<xsl:variable name="text">
			<OPENHOURS>
				<xsl:value-of select="concat('DAYMO - DAYSU: ',replace(replace(text(),'geschlossen',$closed24),'OPEN',$open24))"/>
			</OPENHOURS>
		</xsl:variable>
		<xsl:apply-templates select="$text/OPENHOURS"/>
	</xsl:template>
	<xsl:template match="OPENHOURS">
		<xsl:param name="pText"/>
		<SubSection xmlns="http://www.opentravel.org/OTA/2003/05">
			<xsl:attribute name="SubTitle"><xsl:value-of select="'OfficeHours'"/></xsl:attribute>
			<xsl:choose>
				<xsl:when test="contains(text(),',')">
					<xsl:for-each select="tokenize(.,', ')">
						<xsl:call-template name="run"/>
					</xsl:for-each>
				</xsl:when>
				<xsl:otherwise>
					<xsl:call-template name="run"/>
				</xsl:otherwise>
			</xsl:choose>
		</SubSection>
	</xsl:template>
	<xsl:template name="run">
		<xsl:param name="pText"/>
		<xsl:variable name="frDay">
			<xsl:choose>
				<xsl:when test="contains(substring-before(.,':'),'-')">
					<xsl:value-of select="substring-before(substring-after(substring-before(.,':'),'DAY'),' -')"/>
				</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="substring-after(substring-before(.,':'),'DAY')"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>
		<xsl:variable name="fromDay">
			<xsl:call-template name="mapDay">
				<xsl:with-param name="pDay" select="$frDay"/>
			</xsl:call-template>
		</xsl:variable>
		<xsl:variable name="tDay">
			<xsl:choose>
				<xsl:when test="contains(substring-before(.,':'),'-')">
					<xsl:value-of select="substring-after(substring-before(.,':'),'- DAY')"/>
				</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="substring-after(substring-before(.,':'),'DAY')"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>
		<xsl:variable name="toDay">
			<xsl:call-template name="mapDay">
				<xsl:with-param name="pDay" select="$tDay"/>
			</xsl:call-template>
		</xsl:variable>
		<xsl:variable name="listToTokenize" select="concat(substring-before(concat($fromDay,substring-after($weekDays, $fromDay)), $toDay),$toDay,',')"/>
		<xsl:call-template name="tokenize">
			<xsl:with-param name="pText" select="$listToTokenize"/>
		</xsl:call-template>
	</xsl:template>
	<xsl:template name="tokenize">
		<xsl:param name="pText"/>
		<xsl:if test="string-length($pText)">
			<Paragraph xmlns="http://www.opentravel.org/OTA/2003/05">
				<xsl:attribute name="Name"><xsl:value-of select="substring-before($pText, ',')"/></xsl:attribute>
				<Text xmlns="http://www.opentravel.org/OTA/2003/05">
					<xsl:call-template name="mapHours">
						<xsl:with-param name="pHours" select="substring-after(string(.),': ')"/>
					</xsl:call-template>
				</Text>
			</Paragraph>
			<xsl:call-template name="tokenize">
				<xsl:with-param name="pText" select="substring-after($pText, ',')"/>
			</xsl:call-template>
		</xsl:if>
	</xsl:template>
	<xsl:template name="mapDay">
		<xsl:param name="pDay"/>
		<xsl:choose>
			<xsl:when test="$pDay= 'MO'">Monday</xsl:when>
			<xsl:when test="$pDay = 'TU'">Tuesday</xsl:when>
			<xsl:when test="$pDay = 'WE'">Wednesday</xsl:when>
			<xsl:when test="$pDay = 'TH'">Thursday</xsl:when>
			<xsl:when test="$pDay = 'FR'">Friday</xsl:when>
			<xsl:when test="$pDay = 'SA'">Saturday</xsl:when>
			<xsl:when test="$pDay = 'SU'">Sunday</xsl:when>
		</xsl:choose>
	</xsl:template>
	<!---->
	<!--xsl:template name="setBlock">
		<xsl:variable name="open" select="substring-after(text(),': ')"/>
		<xsl:variable name="openToM" select="substring-after($open,':')"/>
		<xsl:variable name="openTM" select="substring-before(substring-after($open,':'),' -')"/>
		<xsl:variable name="openToHrs" select="concat(substring-after($open,' - '),':00')"/>
		<xsl:variable name="closeToM" select="substring-after($open,' - ')"/>
		<xsl:variable name="openFromHrs" select="concat(substring-before($open,' - '),':00')"/>
		<xsl:variable name="openH" select="xs:time($openToHrs) - xs:time($openFromHrs)"/>
		<xsl:variable name="openHrs" select="substring-before(string(substring-after(string($openH),'PT')),'H')"/>
		<xsl:variable name="randDate" select="'1999-01-01T'"></xsl:variable>
		<xsl:variable name="openH" select="xs:dateTime(concat($randDate,$openToHrs)) - xs:dateTime(concat($randDate,$openFromHrs))"/>
		<xsl:variable name="randDate1" select="xs:dateTime('2013-06-22T01:00:00')"></xsl:variable>
		<xsl:variable name="randDate2" select="xs:dateTime('2013-06-22T23:00:00')"></xsl:variable>
		<xsl:variable name="durationDate" select="$randDate2 - $randDate1"></xsl:variable>
		<xsl:variable name="openMts">
			<xsl:choose>
					<xsl:when test="contains(string($openH),'H')">
						<xsl:value-of select="substring-before(string(substring-after(string(xs:time($openToHrs) - xs:time($openFromHrs)),'H')),'M')"/>
     			</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="substring-before(string(substring-after(string(xs:time($openToHrs) - xs:time($openFromHrs)),'PT')),'M')"/>
				</xsl:otherwise>
			</xsl:choose>				
		</xsl:variable> 
		<xsl:variable name="MtsMultiplikator">
			<xsl:choose>
				<xsl:when test="$openMts =''">0</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="round(xs:double($openMts) div 15)"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>	 
			<xsl:variable name="HrsMultiplikator">
			<xsl:choose>
				<xsl:when test="$openHrs =''">0</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="round(xs:double($openHrs) * 4)"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>	 
		<xsl:variable name="closeToHrsMultipl">
			<xsl:choose>
				<xsl:when test="$open =''">0</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="number(substring-before($open,':'))*4"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>	 
		<xsl:variable name="closeToMtsMultipl">
			<xsl:choose>
				<xsl:when test="$openTM =''">0</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="round(xs:double($openTM) div 15)"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>	 
		<xsl:variable name="OpenQuarters" select="$MtsMultiplikator + $HrsMultiplikator"/>
		<xsl:variable name="CloseQuarters" select="$closeToHrsMultipl + $closeToMtsMultipl"/>
		<xsl:variable name="CloseEnd" select="number('96') - ($OpenQuarters + $CloseQuarters)"/>
		<Text>
			<xsl:call-template name="Schleife">
				<xsl:with-param name="Zaehler" select="number('1')"/>
				<xsl:with-param name="ZaehlerAnz" select="number($CloseQuarters)"/>
				<xsl:with-param name="cHar" select="'C'"/>
			</xsl:call-template>
			<xsl:call-template name="Schleife">
				<xsl:with-param name="Zaehler" select="number('1')"/>
				<xsl:with-param name="ZaehlerAnz" select="number($OpenQuarters)"/>
				<xsl:with-param name="cHar" select="'O'"/>
			</xsl:call-template>
			<xsl:call-template name="Schleife">
				<xsl:with-param name="Zaehler" select="number('1')"/>
				<xsl:with-param name="ZaehlerAnz" select="number($CloseEnd)"/>
				<xsl:with-param name="cHar" select="'C'"/>
			</xsl:call-template>
		</Text>
		
	</xsl:template>->
	<xsl:template name="Schleife">
		<xsl:param name="Zaehler"/>
		<xsl:param name="ZaehlerAnz"/>
		<xsl:param name="cHar"/>
		<xsl:choose>
			<xsl:when test="$Zaehler &lt;= number($ZaehlerAnz)">
				<xsl:value-of select="$cHar"/>
				<xsl:call-template name="Schleife">
					<xsl:with-param name="Zaehler" select="$Zaehler + 1"/>
					<xsl:with-param name="ZaehlerAnz" select="$ZaehlerAnz"/>
					<xsl:with-param name="cHar" select="$cHar"/>
				</xsl:call-template>
			</xsl:when>
			<xsl:otherwise>
				<xsl:call-template name="Abbruch"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>-->
	<xsl:template name="Abbruch">
		<xsl:comment>Schleife beendet!</xsl:comment>
	</xsl:template>
</xsl:transform>
Code:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:f="http://www.traveltainment.de/xslt/functions" xmlns:xs="http://www.w3.org/2001/XMLSchema">
	<!-- Transforms a concatenated sequence of time intervals ('08:13-13:00, 15:00-18:00') 
        into a flag string ('CCCCCCCOOOOOO...') where each flag represents a 15 minutes interval
   -->
	<xsl:function name="f:getOpenFlags" as="xs:string">
		<xsl:param name="openingData" as="xs:string"/>
		<xsl:variable name="intervals" select="tokenize($openingData, ',\s*')"/>
		<xsl:variable name="openFirst" select="substring-before($intervals[1], ' - ')" as="xs:string"/>
		<xsl:variable name="openLast" select="substring-after($intervals[last()], ' - ')" as="xs:string"/>
		<xsl:variable name="initialClosed" select="f:interval2flags('C', '00:00', $openFirst)"/>
		<xsl:variable name="intermediate" select="f:getOpenFlagsRC($intervals)"/>
		<xsl:variable name="finalClosed" select="f:interval2flags('C', $openLast, '24:00')"/>
		<xsl:sequence select="string-join(($initialClosed, $intermediate, $finalClosed), '')"/>
	</xsl:function>
	<!-- Transforms a start time and an end time in a flag string ('CCCC')
        where each flag represents a 15 minutes interval.
   -->
	<xsl:function name="f:interval2flags" as="xs:string">
		<xsl:param name="flag" as="xs:string"/>
		<xsl:param name="start" as="xs:string"/>
		<xsl:param name="end" as="xs:string"/>
		<xsl:variable name="numUnits" as="xs:double">
			<xsl:choose>
				<xsl:when test="$start eq '00:00' and $end eq '24:00'">
					<xsl:sequence select="24 * 4"/>
				</xsl:when>
				<xsl:otherwise>
					<xsl:variable name="startTime" select="xs:time(concat($start, ':00'))" as="xs:time"/>
					<xsl:variable name="endTime" select="xs:time(concat($end, ':00'))" as="xs:time"/>
					<xsl:variable name="diff" select="$endTime - $startTime" as="xs:dayTimeDuration"/>
					<xsl:variable name="duration" select="if ($endTime ne $startTime and $endTime eq xs:time('00:00:00')) then ($diff + xs:dayTimeDuration('PT24H')) else $diff"/>
					<xsl:sequence select="$duration div xs:dayTimeDuration('PT15M')"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>
		<xsl:sequence select="string-join(for $i in 1 to xs:integer($numUnits) return $flag, '')"/>
	</xsl:function>
	<!-- Recursive helper function ofr 'getOpenFlags'.
   -->
	<xsl:function name="f:getOpenFlagsRC" as="xs:string*">
		<xsl:param name="intervals" as="xs:string*"/>
		<xsl:variable name="i1" select="$intervals[1]"/>
		<xsl:variable name="start" select="substring-before($i1, ' - ')"/>
		<xsl:variable name="end" select="substring-after($i1, ' - ' )"/>
		<xsl:variable name="remaining" select="$intervals[position() gt 1]"/>
		<!-- the flags for this "open" interval -->
		<xsl:sequence select="f:interval2flags('O', $start, $end)"/>
		<xsl:if test="exists($remaining)">
			<!-- the flags for the "closed" between this interval and the next one -->
			<xsl:sequence select="f:interval2flags('C', $end, substring-before($remaining[1], ' - '))"/>
			<!-- the flags for the remaining intervals -->
			<xsl:sequence select="f:getOpenFlagsRC($remaining)"/>
		</xsl:if>
	</xsl:function>
	<!--xsl:template match="/">
      <xsl:variable name="testData" select="'00:00 - 24:00'" as="xs:string"/>     
            <xsl:sequence select="f:getOpenFlags($testData)"/>
   </xsl:template-->
	<xsl:template name="mapHours">
		<xsl:param name="pHours"/>
		<xsl:sequence select="f:getOpenFlags($pHours)"/>
	</xsl:template>
</xsl:transform>

It is somehow working when input is:

Code:
<PickUpOpeningHours>07:00 - 24:00</PickUpOpeningHours>
But fails when the input is:

Code:
 <PickUpOpeningHours>08:30 - 12:00, 14:30 - 18:00</PickUpOpeningHours>
I am finding it difficult to debug. Could you please give it a look and identify the problem?

Thanks.
 
Old May 22nd, 2013, 10:15 AM
Authorized User
 
Join Date: May 2009
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
Default

any pointer for my last example script?
 
Old May 22nd, 2013, 10:39 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

I can't debug it for you, but I can make a couple of suggestions from glancing at your code.

Firstly, constructs like this:

Code:
<xsl:variable name="MtsMultiplikator">
			<xsl:choose>
				<xsl:when test="$openMts =''">0</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="round(xs:double($openMts) div 15)"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>
in XSLT 2.0 can be better written as

Code:
<xsl:variable name="MtsMultiplikator" select="if ($openMts =''') then 0 else round(xs:double($openMts) div 15)"/>
This has several benefits: it makes your code a lot shorter, making it easier to see the bugs, and it's also more efficient because the variable is now a number, not a result tree fragment.

Secondly, the conversion from a time such as 08:00 to a number of 15-minute periods since midnight can probably be best expressed as

Code:
((xs:time(concat($t, ':00)) - xs:time('00:00:00')) div xs:dayTimeDuration('PT15M')
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference





Similar Threads
Thread Thread Starter Forum Replies Last Post
help on xslt transformation li72 XSLT 2 March 4th, 2008 10:14 AM
Help on XSLT transformation li72 XSLT 2 February 22nd, 2008 12:33 PM
help with xslt transformation li72 XSLT 6 November 19th, 2007 01:51 PM
XSLT transformation yengzhai XSLT 1 April 21st, 2005 05:51 AM
XSLT transformation causes ^M reddygaru XSLT 2 December 16th, 2003 10:41 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.