I am still working on the first beta version of the Krakowpat's Blogware. Unfortunately, I am not yet working within my krakowpat-blogware project on Goggle Code Project Hosting. To be honest, I have just starting to read the Subversion documentation, which is a new topic for me... I am currently using Microsoft Visual Source Safe as version control repository. Anyway, I wanted to share with you my first challenge in finding the right XPath for my main XSL script.
In one sentence, the architecture of the Krakowpat’s blogware is: A unique XML document processed by a (set of) XSL script(s). All the data of my blog are in one single XML document, and the different HTML pages you are reading on my web site are the results of its processing by (set of) XSL script(s). The general structure of the XML document looks like the following example:
<Posts>
<Post ID="post7">This is the content of the 7th post.</Post>
<Post ID="post6">This is the content of the 6th post.</Post>
<Post ID="post5">This is the content of the 5th post.</Post>
<Post ID="post4">This is the content of the 4th post.</Post>
<Post ID="post3">This is the content of the 3rd post.</Post>
<Post ID="post2">This is the content of the 2nd post.</Post>
<Post ID="post1">This is the content of the 1st post.</Post>
</Posts>
First, I started to develop a XSL script to create the root index page of my blog using a XPath like:
/Posts/Post[position()<=5]
which is giving the following result when applied to the example above:
<Post ID="post7">This is the content of the 7th post.</Post>
<Post ID="post6">This is the content of the 6th post.</Post>
<Post ID="post5">This is the content of the 5th post.</Post>
<Post ID="post4">This is the content of the 4th post.</Post>
<Post ID="post3">This is the content of the 3rd post.</Post>
For the pages with individual post, I then created a second XSL script that was more or less a copy-paste of the first one, but with a XPath like:
/Posts/Post[@ID='post7']
where the string 'post7' is passed as a XSL parameter. It gives the following result when applied to the example above:
<Post ID="post7">This is the content of the 7th post.</Post>
After the completion of this 1st version of my blogware with two different XSL scripts, one to generate the index page and another one to generate the individual post pages, I found that having two XSL scripts looking the same for 99.5% of their content will be a pain to maintain! But, in order to merge the two scripts into a single one, the challenge was to combine the two XPaths described above... and I like XPath challenges!
I started with the following stupid combination, which obviously does not work:
/Posts/Post[@ID='post7' or position()<=5]
and start to thing about an elegant working solution... From this stupid combination, if you want to retrieve the result of the 2nd XPath, you need to cancel the effect of the second part of the or, which can be easily done by adding and false() to it:
/Posts/Post[@ID='post7' or (position()<=5 and false())]
On the other hand, if you want to retrieve the result of the 1st XPath, you need to cancel the effect of the first part of the or,which can be easily done by taking an @ID that does not exist in the XML document, something like 'index' for instance (in the XSL, this string is a parameter), while the and false() should be changed into and true():
/Posts/Post[@ID='index' or (position()<=5 and true())]
The elegant (and working) combination is now close. I am just missing a kind of "function" with the result false() when its parameter is an @ID that exists in the XML document, and true() when it does not exist. Indeed, we can just count the number of <Post> with the given @ID, and compare it to zero:
count(//Post[@ID='post7'])=0) is false()
and
count(//Post[@ID='index'])=0) is true()
Thus, I can finally write that, on one hand, the XPath:
/Posts/Post[@ID='index' or (position()<=5 and count(//Post[@ID='index'])=0)]
gives the same result as the first XPath:
<Post ID="post7">This is the content of the 7th post.</Post>
<Post ID="post6">This is the content of the 6th post.</Post>
<Post ID="post5">This is the content of the 5th post.</Post>
<Post ID="post4">This is the content of the 4th post.</Post>
<Post ID="post3">This is the content of the 3rd post.</Post>
while the XPath:
/Posts/Post[@ID='post7' or (position()<=5 and count(//Post[@ID='post7'])=0)]
gives the same result as the second XPath:
<Post ID="post7">This is the content of the 7th post.</Post>
The maintenance is now easier since I have only one single parameterised XSL script for the generation of the index page as well as for all the individual post pages, using the following XPath:
Posts/Post[@ID=$ID or (position()<=5 and count(//Post[@ID=$ID])=0)]
and it was good fun to find it ;-)
