Forum Moderators: open

Message Too Old, No Replies

What Time Is It?

Newton wants to know where Mickey's little hand is.

         

cmarshall

3:41 pm on Mar 31, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Okay, I just another nasty little wall.

I have a duration value (01:15:00), and I want to extract the hours and minutes from it (I don't have a weetest example of it yet. I'll post one a bit later).

I had used a fairly clumsy string parser to do this. Works fine:

<xsl:param name="time" select="01:15:00"/>
<xsl:variable name="hours" select="substring-before($time, ':')"/>
<xsl:variable name="minutes" select="substring-before(substring-after($time, ':'), ':')"/>

This works well. $hours contains "01", and $minutes contains "15".

Now, as I was learning XPath, I came upon these cute little functions [w3schools.com].

So, I replaced the clumsy strings above with:

<xsl:param name="time" select="01:15:00"/>
<xsl:variable name="hours" select="hours-from-time($time)"/>
<xsl:variable name="minutes" select="minutes-from-time($time)"/>

I ran it through <oXygen>, and everything looks great.

However, when I run it through PHP's XSLT processor, barf city.

I need to do a bit more research into PHP's XML/XSLT support. I want to see if I can mirror it in <oXygen>.

The official spec [w3.org] says it should be OK:

hours-from-time [w3.org]

minutes-from-time [w3.org]

I'll have to see if there's something else I need to do to make PHP's XSLT processor work. Maybe I can feed it a schema.

cmarshall

8:19 pm on Mar 31, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Well, Zend talks about it here [devzone.zend.com].

It seems that if I use Xsltproc [xmlsoft.org], as opposed to xalan [xalan.apache.org] or saxon [saxon.sourceforge.net], I will get results similar to what PHP gives me with libxslt [xml.com].

<oXygen/> likes to default to saxon.

cmarshall

9:59 pm on Mar 31, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Okay here's a weetest example of the operation that works in libxslt:

First, we have the XML file. Note the new elements I added:

<?xml version="1.0" encoding="ISO-8859-1"?>
<weetest xmlns="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com file:/Users/cmarshall/Desktop/XMLTest/wee_test/wee_test.xsd">
<elementwrapper id="1">
<data_cat>1</data_cat>
<data_duration>01:05:00</data_duration>
<data_name>Test (cat 1)</data_name>

</elementwrapper>
<elementwrapper id="2">
<data_cat>1</data_cat>
<data_name>Test (cat 1)</data_name>
<data_uri/>
</elementwrapper>
<elementwrapper id="3">
<data_cat>2</data_cat>
<data_duration>21:35:00</data_duration>
<data_name>Test (cat 2)</data_name>
</elementwrapper>
<elementwrapper id="4">
<data_cat>2</data_cat>
<data_name>Test (cat 2)</data_name>

</elementwrapper>
<elementwrapper id="5">
<data_cat>3</data_cat>
<data_name>Test (cat 3)</data_name>
<data_uri/>
</elementwrapper>
<elementwrapper id="6">
<data_cat>3</data_cat>
<data_duration>11:01:20</data_duration>
<data_name>Test (cat 3)</data_name>
</elementwrapper>
</weetest>

I altered the schema to accommodate it (It's an optional element):

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com"
elementFormDefault="qualified">
<xs:element name="weetest">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="elementwrapper">
<xs:complexType>
<xs:sequence>
<xs:element name="data_cat" type="xs:integer"/>
<xs:element name="data_duration" minOccurs="0" type="xs:time"/>
<xs:element name="data_name" type="xs:string"/>
<xs:element minOccurs="0" name="data_uri" type="xs:anyURI"/>
</xs:sequence>
<xs:attribute name="id" type="xs:integer" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

And now, a stylesheet that has been pretty radically altered to display a plain-English readout of the duration:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wt="http://www.example.com" exclude-result-prefixes="wt">
<xsl:output indent="yes" method="html" version="4.01" doctype-system="http://www.w3c.org/tr/html4/strict.dtd" doctype-public="-//W3c//DTD HTML 4.01//EN"/>

<xsl:template match="/">
<html>
<head>
<title>XML Test</title>
</head>
<body>
<h1>Category 1</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_cat=1]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
<h1>Category 2</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_cat=2]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
<h1>Category 3</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_cat=3]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
</body>
</html>
</xsl:template>

<xsl:template name="render_one">
<xsl:element name="div">
<xsl:attribute name="class">
<xsl:text>alt_</xsl:text>
<xsl:number value="position() mod 2" format="1"/>
</xsl:attribute>
<xsl:call-template name="display_duration">
<xsl:with-param name="duration" select="wt:data_duration"/>
</xsl:call-template>

<div class="display">
<xsl:choose>
<xsl:when test="wt:data_uri!= ''">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of disable-output-escaping="yes" select="wt:data_uri"/>
</xsl:attribute>
<xsl:value-of select="wt:data_name"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="wt:data_name"/>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:element>
</xsl:template>

<xsl:template name="display_duration">
<xsl:param name="duration"/>
<xsl:if test="$duration!= ''">
<xsl:variable name="hours" select="substring-before($duration, ':')"/>
<xsl:variable name="minutes" select="substring-before(substring-after($duration, ':'), ':')"/>
<xsl:element name="div">
<xsl:attribute name="class">duration_display</xsl:attribute>
<xsl:text>The duration is </xsl:text>
<xsl:if test="floor($hours)>0">
<xsl:value-of select="floor($hours)"/>
<xsl:choose>
<xsl:when test="floor($hours)>1">
<xsl:text> hours</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> hour</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:if test="floor($minutes)>0">
<xsl:if test="floor($hours)>0">
<xsl:text> and </xsl:text>
</xsl:if>
<xsl:value-of select="floor($minutes)"/>
<xsl:choose>
<xsl:when test="floor($minutes)>1">
<xsl:text> minutes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> minute</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:text> long.</xsl:text>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

I was able to cram some extra examples in there, such as calling the template with a parameter and using "variables."

One important distinction between parameters and "variables" is that parameters were designed to pass values from one context to another, so they need to be declared at the beginning of the template. A "variable" is meant to be used within a context, so it can be declared in many more places.

This works in libxslt/PHP5, and generates the following output:

<!DOCTYPE html PUBLIC "-//W3c//DTD HTML 4.01//EN" "http://www.w3c.org/tr/html4/strict.dtd">
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>XML Test</title>
</head>
<body>
<h1>Category 1</h1>
<div class="alt_1">
<div class="duration_display">The duration is 1 hour and 5 minutes long.</div>
<div class="display">
<a href="http://www.example.com/wee_test.html">Test (cat 1)</a>
</div>
</div>
<div class="alt_0">
<div class="display">Test (cat 1)</div>
</div>
<h1>Category 2</h1>
<div class="alt_1">
<div class="duration_display">The duration is 21 hours and 35 minutes long.</div>
<div class="display">Test (cat 2)</div>
</div>
<div class="alt_0">
<div class="display">
<a href="http://www.example.com/wee_test.html">Test (cat 2)</a>
</div>
</div>
<h1>Category 3</h1>
<div class="alt_1">
<div class="display">Test (cat 3)</div>
</div>
<div class="alt_0">
<div class="duration_display">The duration is 11 hours and 1 minute long.</div>
<div class="display">Test (cat 3)</div>
</div>
</body>
</html>

cmarshall

10:11 pm on Mar 31, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Okay, now this version of the
display_duration
template will not work in libxslt/PHP5 and Xalan, but works in Saxon8A:

<xsl:template name="display_duration">
<xsl:param name="duration"/>
<xsl:if test="$duration!= ''">
<xsl:variable name="hours" select="hours-from-time($duration)"/>
<xsl:variable name="minutes" select="minutes-from-time($duration)"/>

<xsl:element name="div">
<xsl:attribute name="class">duration_display</xsl:attribute>
<xsl:text>The duration is </xsl:text>
<xsl:if test="floor($hours)>0">
<xsl:value-of select="floor($hours)"/>
<xsl:choose>
<xsl:when test="floor($hours)>1">
<xsl:text> hours</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> hour</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:if test="floor($minutes)>0">
<xsl:if test="floor($hours)>0">
<xsl:text> and </xsl:text>
</xsl:if>
<xsl:value-of select="floor($minutes)"/>
<xsl:choose>
<xsl:when test="floor($minutes)>1">
<xsl:text> minutes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> minute</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:text> long.</xsl:text>
</xsl:element>
</xsl:if>
</xsl:template>

Because it doesn't work in default PHP, it's worthless to me.

cmarshall

10:23 pm on Mar 31, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Okay, just to get fancy, I added two more categories, and use XPath to filter for them:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wt="http://www.example.com" exclude-result-prefixes="wt">
<xsl:output indent="yes" method="html" version="4.01" doctype-system="http://www.w3c.org/tr/html4/strict.dtd" doctype-public="-//W3c//DTD HTML 4.01//EN"/>

<xsl:template match="/">
<html>
<head>
<title>XML Test</title>
</head>
<body>
<h1>Category 1</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_cat=1]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
<h1>Category 2</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_cat=2]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
<h1>Category 3</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_cat=3]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
<h1>More Than Ten Hours</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[(floor(substring-before(wt:data_duration, ':'))+floor(substring-before(substring-after(wt:data_duration, ':'),':')))>10]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
<h1>Has Links</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_uri!='']">
<xsl:call-template name="render_one"/>
</xsl:for-each>

</body>
</html>
</xsl:template>

<xsl:template name="render_one">
<xsl:element name="div">
<xsl:attribute name="class">
<xsl:text>alt_</xsl:text>
<xsl:number value="position() mod 2" format="1"/>
</xsl:attribute>
<xsl:call-template name="display_duration">
<xsl:with-param name="duration" select="wt:data_duration"/>
</xsl:call-template>
<div class="display">
<xsl:choose>
<xsl:when test="wt:data_uri!= ''">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of disable-output-escaping="yes" select="wt:data_uri"/>
</xsl:attribute>
<xsl:value-of select="wt:data_name"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="wt:data_name"/>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:element>
</xsl:template>

<xsl:template name="display_duration">
<xsl:param name="duration"/>
<xsl:if test="$duration!= ''">
<xsl:variable name="hours" select="substring-before($duration, ':')"/>
<xsl:variable name="minutes" select="substring-before(substring-after($duration, ':'), ':')"/>
<xsl:element name="div">
<xsl:attribute name="class">duration_display</xsl:attribute>
<xsl:text>The duration is </xsl:text>
<xsl:if test="floor($hours)>0">
<xsl:value-of select="floor($hours)"/>
<xsl:choose>
<xsl:when test="floor($hours)>1">
<xsl:text> hours</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> hour</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:if test="floor($minutes)>0">
<xsl:if test="floor($hours)>0">
<xsl:text> and </xsl:text>
</xsl:if>
<xsl:value-of select="floor($minutes)"/>
<xsl:choose>
<xsl:when test="floor($minutes)>1">
<xsl:text> minutes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> minute</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:text> long.</xsl:text>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

This results in the following output:

<!DOCTYPE html PUBLIC "-//W3c//DTD HTML 4.01//EN" "http://www.w3c.org/tr/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>XML Test</title>
</head>
<body>
<h1>Category 1</h1>
<div class="alt_1">
<div class="duration_display">The duration is 1 hour and 5 minutes long.</div>
<div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 1)</a></div>
</div>
<div class="alt_0"><div class="display">Test (cat 1)</div></div>
<h1>Category 2</h1>
<div class="alt_1">
<div class="duration_display">The duration is 21 hours and 35 minutes long.</div>
<div class="display">Test (cat 2)</div>
</div>
<div class="alt_0"><div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 2)</a></div></div>
<h1>Category 3</h1>
<div class="alt_1"><div class="display">Test (cat 3)</div></div>
<div class="alt_0">
<div class="duration_display">The duration is 11 hours and 1 minute long.</div>
<div class="display">Test (cat 3)</div>
</div>
<h1>More Than Ten Hours</h1>
<div class="alt_1">
<div class="duration_display">The duration is 21 hours and 35 minutes long.</div>
<div class="display">Test (cat 2)</div>
</div>
<div class="alt_0">
<div class="duration_display">The duration is 11 hours and 1 minute long.</div>
<div class="display">Test (cat 3)</div>
</div>
<h1>Has Links</h1>
<div class="alt_1">
<div class="duration_display">The duration is 1 hour and 5 minutes long.</div>
<div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 1)</a></div>
</div>
<div class="alt_0"><div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 2)</a></div></div>
</body>
</html>

Note the awful butchering I had to do to the filter for the duration. The official technical term is "Kludge" (US) or "Lash-Up" (UK).

cmarshall

10:35 pm on Mar 31, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Also, just to put some things to the test, I altered the original wee_test.xml file to have a couple more elements. Note that they are out of sequence, as far as the category goes:

<?xml version="1.0" encoding="ISO-8859-1"?>
<weetest xmlns="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com file:/Users/cmarshall/Desktop/XMLTest/wee_test/wee_test.xsd">
<elementwrapper id="1">
<data_cat>1</data_cat>
<data_duration>01:05:00</data_duration>
<data_name>Test (cat 1)</data_name>

</elementwrapper>
<elementwrapper id="2">
<data_cat>1</data_cat>
<data_name>Test (cat 1)</data_name>
<data_uri/>
</elementwrapper>
<elementwrapper id="3">
<data_cat>2</data_cat>
<data_duration>21:35:00</data_duration>
<data_name>Test (cat 2)</data_name>
</elementwrapper>
<elementwrapper id="4">
<data_cat>2</data_cat>
<data_name>Test (cat 2)</data_name>

</elementwrapper>
<elementwrapper id="5">
<data_cat>3</data_cat>
<data_name>Test (cat 3)</data_name>
<data_uri/>
</elementwrapper>
<elementwrapper id="6">
<data_cat>3</data_cat>
<data_duration>11:01:20</data_duration>
<data_name>Test (cat 3)</data_name>
</elementwrapper>
<elementwrapper id="7">
<data_cat>2</data_cat>
<data_duration>10:00:01</data_duration>
<data_name>Test (cat 2)</data_name>
</elementwrapper>
<elementwrapper id="8">
<data_cat>1</data_cat>
<data_duration>10:01:00</data_duration>
<data_name>Test (cat 1)</data_name>
</elementwrapper>

</weetest>

This results in the following output:

<!DOCTYPE html PUBLIC "-//W3c//DTD HTML 4.01//EN" "http://www.w3c.org/tr/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>XML Test</title>
</head>
<body>
<h1>Category 1</h1>
<div class="alt_1">
<div class="duration_display">The duration is 1 hour and 5 minutes long.</div>
<div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 1)</a></div>
</div>
<div class="alt_0"><div class="display">Test (cat 1)</div></div>
<div class="alt_1">
<div class="duration_display">The duration is 10 hours and 1 minute long.</div>
<div class="display">Test (cat 1)</div>

</div>
<h1>Category 2</h1>
<div class="alt_1">
<div class="duration_display">The duration is 21 hours and 35 minutes long.</div>
<div class="display">Test (cat 2)</div>
</div>
<div class="alt_0"><div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 2)</a></div></div>
<div class="alt_1">
<div class="duration_display">The duration is 10 hours long.</div>
<div class="display">Test (cat 2)</div>
</div>

<h1>Category 3</h1>
<div class="alt_1"><div class="display">Test (cat 3)</div></div>
<div class="alt_0">
<div class="duration_display">The duration is 11 hours and 1 minute long.</div>
<div class="display">Test (cat 3)</div>
</div>
<h1>More Than Ten Hours</h1>
<div class="alt_1">
<div class="duration_display">The duration is 21 hours and 35 minutes long.</div>
<div class="display">Test (cat 2)</div>
</div>
<div class="alt_0">
<div class="duration_display">The duration is 11 hours and 1 minute long.</div>
<div class="display">Test (cat 3)</div>
</div>
<div class="alt_1">
<div class="duration_display">The duration is 10 hours and 1 minute long.</div>
<div class="display">Test (cat 1)</div>

</div>
<h1>Has Links</h1>
<div class="alt_1">
<div class="duration_display">The duration is 1 hour and 5 minutes long.</div>
<div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 1)</a></div>
</div>
<div class="alt_0"><div class="display"><a href="http://www.example.com/wee_test.html">Test (cat 2)</a></div></div>
</body>
</html>

Also note that ten hours and one second didn't count, because we aren't paying attention to seconds.

daveVk

2:38 am on Apr 1, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Assume categories can be done in loop of some form?


<xsl:for-each ...
<h1>Category ?</h1>
<xsl:for-each select="wt:weetest/wt:elementwrapper[wt:data_cat=?]">
<xsl:call-template name="render_one"/>
</xsl:for-each>
</xsl:for-each>

Also note you have adjacent alt_1 divs (granted with h1 between), this a challenging language

cmarshall

2:51 am on Apr 1, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



You are correct. Categories are next [webmasterworld.com].

I'm not sure how to continue the alt_X repetitions. That requires the ability to transfer a value from inside one context, up to its parent, then down into another context.

From what I've seen of XSLT/XPath so far, we don't have that capability.

As someone who has spent a couple of decades using a multitude of languages from machine language to Pascal, to C, to PL/1 (blast from the past, anyone?), etc... with about twenty years of C++, this is rather a jarring thing to come against.

I mean, I remember punching hex code into a keypad nailed onto a piece of wood twenty-five years ago that could transfer values from one context to another.

I can't believe that this capability was left out of XSLT. If it was, it was for a good reason. I just don't see it at the moment.

Ya know, it would be really great if one of the real, experienced XML people were to pop in here and give poor ol' Newton the n00b a hand. He's banged his shins up pretty good stumbling around in the dark here...