<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss'><id>tag:blogger.com,1999:blog-23815417</id><updated>2009-11-23T19:09:33.327Z</updated><title type='text'>Thoughts on XSLT</title><subtitle type='html'>Andrew Welch</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default?start-index=26&amp;max-results=25'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>52</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-23815417.post-7627172438027645656</id><published>2009-11-17T10:10:00.004Z</published><updated>2009-11-17T10:20:46.914Z</updated><title type='text'>Mark Logic adds XSLT support</title><content type='html'>Mark Logic currently lacks any support for XSLT... but that's about to change: Norm Walsh has &lt;a href="http://norman.walsh.name/2009/11/12/nymug"&gt;announced upcoming support for it&lt;/a&gt; in version 5.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-7627172438027645656?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/7627172438027645656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=7627172438027645656&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7627172438027645656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7627172438027645656'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2009/11/mark-logic-adds-xslt-support.html' title='Mark Logic adds XSLT support'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-7530273833563764865</id><published>2009-09-14T12:54:00.002Z</published><updated>2009-09-14T13:06:55.378Z</updated><title type='text'>XML Schema 1.1 tutorials</title><content type='html'>Roger Costello has created a couple of good &lt;a href="http://www.xfront.com/xml-schema-1-1/"&gt;powerpoint presentations on XSD 1.1&lt;/a&gt;&lt;br /&gt;One tutorial is for developers, the other for managers which contains more general wordy descriptions of the benefits of 1.1&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-7530273833563764865?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.xfront.com/xml-schema-1-1/' title='XML Schema 1.1 tutorials'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/7530273833563764865/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=7530273833563764865&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7530273833563764865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7530273833563764865'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2009/09/xml-schema-11-tutorials.html' title='XML Schema 1.1 tutorials'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-3055359810785425324</id><published>2008-08-22T14:56:00.000Z</published><updated>2008-08-22T15:03:56.318Z</updated><title type='text'>Some sample templates for use with LexEv</title><content type='html'>&lt;p&gt;If your XML has been parsed using &lt;a href="http://andrewjwelch.com/lexev"&gt;LexEv&lt;/a&gt;, here are some sample templates for handling the LexEv markup.&lt;br /&gt;&lt;p&gt;To output an entity reference:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;xsl:template match="lexev:entity"&amp;gt;&lt;br /&gt;  &amp;lt;xsl:value-of disable-output-escaping="yes" select="concat('&amp;amp;amp;', @name, ';')"/&amp;gt;    &lt;br /&gt;&amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To process a CDATA section as markup:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;xsl:template match="lexev:cdata"&amp;gt;&lt;br /&gt;  &amp;lt;xsl:apply-templates/&amp;gt;    &lt;br /&gt;&amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;    &lt;br /&gt;&lt;p&gt;To output a DOCTYPE from the processing instructions:&lt;br /&gt;&lt;p&gt;In XSLT 1.0 the doctype-public and doctype-system attributes on &lt;code&gt;xsl:output&lt;/code&gt; are static and need to be known at compile time, which means I'm afraid you have to do this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;xsl:template match="/"&gt;&lt;br /&gt; &amp;lt;xsl:value-of disable-output-escaping="yes"&lt;br /&gt;  select="concat('&amp;amp;lt;!DOCTYPE ', name(/*), '&amp;amp;#xa;  PUBLIC &amp;amp;quot;', &lt;br /&gt;   processing-instruction('doctype-public'), '&amp;amp;quot; &amp;amp;quot;',&lt;br /&gt;   processing-instruction('doctype-system'), '&amp;amp;quot;&amp;amp;gt;')"/&amp;gt;&lt;br /&gt; &amp;lt;xsl:apply-templates/&amp;gt;&lt;br /&gt;&amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;   &lt;br /&gt;&lt;p&gt;In XSLT 2.0 you can use &lt;code&gt;xsl:result-document&lt;/code&gt; where the doctype-public and doctype-system are AVTs which mean their values can be determined at runtime:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;xsl:template match="/"&amp;gt;&lt;br /&gt; &amp;lt;xsl:result-document&lt;br /&gt;  doctype-public="{processing-instruction('doctype-public')}"&lt;br /&gt;  doctype-system="{processing-instruction('doctype-system')}"&amp;gt;&lt;br /&gt;  &amp;lt;xsl:apply-templates/&amp;gt;&lt;br /&gt; &amp;lt;/xsl:result-document&amp;gt;&lt;br /&gt;&amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-3055359810785425324?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/3055359810785425324/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=3055359810785425324&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/3055359810785425324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/3055359810785425324'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/08/some-sample-templates-for-use-with.html' title='Some sample templates for use with LexEv'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2947874638788408237</id><published>2008-08-21T15:34:00.004Z</published><updated>2008-08-21T16:06:53.374Z</updated><title type='text'>LexEv XMLReader - converts lexical events into markup</title><content type='html'>It's often a requirement to preserve entity references through to the output (which are usually lost during parsing) or to process the contents of CDATA sections as markup.  The &lt;a href="http://andrewjwelch.com/lexev/"&gt;&lt;span style="font-style: italic;"&gt;Lex&lt;/span&gt;ical &lt;span style="font-style: italic;"&gt;Ev&lt;/span&gt;ent XMLReader&lt;/a&gt;  wraps the standard XMLReader to convert lexical events into markup so that they can be processed.  Typical uses are:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Converting cdata sections into markup: &lt;p&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;![CDATA[ &amp;amp;lt;p&amp;amp;gt; a para &amp;amp;lt;p&amp;amp;gt; ]]&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;to:&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;lexev:cdata&amp;gt; &amp;lt;p&amp;gt; a para &amp;lt;/p&amp;gt; &amp;lt;/lexev:cdata&amp;gt;&lt;/code&gt;&lt;/p&gt;   &lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Preserving entity references:&lt;p&gt;&lt;br /&gt;&lt;code&gt;hello&amp;amp;mdash;world&lt;/code&gt;&lt;/p&gt;&lt;p&gt;is converted to:&lt;/p&gt;&lt;p&gt;&lt;code&gt;hello&amp;lt;lexev:entity name="mdash"&amp;gt;—&amp;lt;/lexev:entity&amp;gt;world&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Preserving the doctype declaration:&lt;p&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"&lt;br /&gt;"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;is converted to processing instructions:&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;?doctype-public -//W3C//DTD XHTML 1.0 Transitional//EN?&amp;gt;&lt;br /&gt;&amp;lt;?doctype-system http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd?&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Marking up comments: &lt;p&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;!-- a comment --&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;is converted to:&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;lexev:comment&amp;gt; a comment &amp;lt;/lexev:comment&amp;gt;&lt;span style="font-family:Georgia,serif;"&gt;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;To use LexEvXMLReader with Saxon:&lt;/p&gt;&lt;br /&gt;&lt;code&gt;java -cp saxon9.jar;&lt;b&gt;LexEvXMLReader.jar&lt;/b&gt; net.sf.saxon.Transform &lt;b&gt;-x:com.andrewjwelch.lexev.LexEvXMLReader&lt;/b&gt; input.xml&lt;br /&gt;stylesheet.xslt&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Make sure &lt;code&gt;&lt;b&gt;LexEvXMLReader.jar&lt;/b&gt;&lt;/code&gt; is on the classpath, and then tell Saxon to use it with the -x switch (copy and paste this line &lt;code&gt;&lt;b&gt;-x:com.andrewjwelch.lexev.LexEvXMLReader&lt;/b&gt;&lt;code&gt;)&lt;span style="font-family:Georgia,serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;code&gt;&lt;code&gt;&lt;span style="font-family:Georgia,serif;"&gt;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;To use LexEvXMLReader from Java:&lt;/p&gt;&lt;code&gt;XMLReader xmlReader = new LexEvXMLReader();&lt;/code&gt; &lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;You can control the following features of LexEv:&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;enable/disable the marking up of entity references&lt;/li&gt;&lt;br /&gt;&lt;li&gt;enable/disable the marking up of CDATA sections&lt;/li&gt;&lt;br /&gt;&lt;li&gt;set the default namespace for the CDATA section markup&lt;/li&gt;&lt;br /&gt;&lt;li&gt;enable/disable the reporting of the DOCTYPE&lt;/li&gt;&lt;br /&gt;&lt;li&gt;enable/disable the marking up of comments&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;You can set these through the API (if you are including LexEv in an application), or from the command line using the following system properties:&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;com.andrewjwelch.lexev.inline-entities&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;com.andrewjwelch.lexev.cdata&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;com.andrewjwelch.doctype.cdataNamespace&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;com.andrewjwelch.lexev.doctype&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;com.andrewjwelch.lexev.comments&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;For example to set a system property from the command line you would use: &lt;code&gt;-Dcom.andrewjwelch.lexev.comments=false&lt;/code&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;For support, suggestions and licensing, email lexev@andrewjwelch.com&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2947874638788408237?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://andrewjwelch.com/lexev/' title='LexEv XMLReader - converts lexical events into markup'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2947874638788408237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2947874638788408237&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2947874638788408237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2947874638788408237'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/08/lexev-xmlreader-converts-lexical-events.html' title='LexEv XMLReader - converts lexical events into markup'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2957718730635844125</id><published>2008-07-18T14:58:00.000Z</published><updated>2008-07-18T15:21:37.876Z</updated><title type='text'>Kernow 1.6.1</title><content type='html'>&lt;a href="http://kernowforsaxon.sf.net/"&gt;Kernow 1.6.1&lt;/a&gt; (beta) is now availble both as a &lt;a href="http://kernowforsaxon.sf.net/"&gt;download &lt;/a&gt;and via &lt;a href="http://kernowforsaxon.sourceforge.net/jws/Kernow.jnlp"&gt;web start&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Notable things in this release:&lt;br /&gt;&lt;br /&gt;- Line numbers on the editor panes in the sandboxes (thanks to a new version of &lt;a href="http://www.edankert.com/bounce/index.html"&gt;Bounce&lt;/a&gt;). You might not think so, but getting line numbers down the side of the editor pane is really involved.  It's like block indenting (pressing tab or shift-tab when a block of text is selected) in that it's very low level and requires a lot of coding.  Why it's not an intergral part of the editor pane I don't know... &lt;br /&gt;&lt;br /&gt;- Improved the syntax-checking-as-you-type and highlighting, and added the ability to disable it.&lt;br /&gt;&lt;br /&gt;- The output area is now also a JEditorPane using Bounce so it supports tag highlighting.  This might slow things down because now it's an HTML document where every addition is inserted at the end of the document, instead of just appending to a JTextArea... if this proves to be A Bad Thing I'll revert it back to a plain old text area with plain text.&lt;br /&gt;&lt;br /&gt;- You can now select which tabs are visible (in options -&gt; tabs) so if you never use certain tabs (like Batch or Schematron) you can remove them.&lt;br /&gt;&lt;br /&gt;- If you have Saxon SA you can use XML Schema 1.1 (options -&gt; validation)&lt;br /&gt;&lt;br /&gt;- Improved the parameters dialog to make it less fiddly to enter params&lt;br /&gt;&lt;br /&gt;- Slight graphical tweaks and likely other things that I've forgotten...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2957718730635844125?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://kernowforsaxon.sf.net/' title='Kernow 1.6.1'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2957718730635844125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2957718730635844125&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2957718730635844125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2957718730635844125'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/07/kernow-161.html' title='Kernow 1.6.1'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-7950551531493839968</id><published>2008-07-17T11:11:00.001Z</published><updated>2008-07-17T11:36:56.654Z</updated><title type='text'>The Nimbus Look and Feel</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ypDtgn03LEU/SH8usKbvi0I/AAAAAAAAACM/ceAeHx4-cwA/s1600-h/kernow-nimbus.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_ypDtgn03LEU/SH8usKbvi0I/AAAAAAAAACM/ceAeHx4-cwA/s400/kernow-nimbus.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5223945429022903106" /&gt;&lt;/a&gt;This is "Nimbus" - the new look and feel that comes with Java 6 Update 10.  This is a cross platform l&amp;f which means it should look the same on all platforms.  Kernow currently uses the "platform default" look and feel so it should look like a native app on the platform it's run on, but it's hard to make sure it looks right - often what looks ok on Windows will have obscured buttons on Linux... something I should've fixed but never did.&lt;br /&gt;&lt;br /&gt;Anyway, what do you think?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-7950551531493839968?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/7950551531493839968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=7950551531493839968&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7950551531493839968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7950551531493839968'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/07/this-is-nimbus-new-look-and-feel-that.html' title='The Nimbus Look and Feel'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_ypDtgn03LEU/SH8usKbvi0I/AAAAAAAAACM/ceAeHx4-cwA/s72-c/kernow-nimbus.png' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2760403851453168673</id><published>2008-07-11T15:37:00.001Z</published><updated>2008-07-11T16:09:00.763Z</updated><title type='text'>Validating co-constrains in XML Schema 1.1 using xs:alternative</title><content type='html'>Rather than mess around with loads of assertions to check your co-constraints, XML Schema 1.1 introduces the &lt;code&gt;xs:alternative&lt;/code&gt; instruction which allows you to change the type used to validate the element based on some condition.  Instead of defining one type and then adding assertions to check the variations, just define one type per variation, then assign that type based on the condition.  &lt;br /&gt;&lt;br /&gt;To do this you first have to define a default type, then define types for each variation by restricting that type.  To choose between them, use &lt;code&gt;xs:alternative&lt;/code&gt; as a child of &lt;code&gt;xs:element&lt;/code&gt;.  Here's an example of a co-constraint - &lt;code&gt;this&lt;/code&gt; and &lt;code&gt;that&lt;/code&gt; are allowed based on the value of the &lt;code&gt;type&lt;/code&gt; attribute of &lt;code&gt;node&lt;/code&gt; - and how to validate it:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&amp;lt;root&gt;&lt;br /&gt;  &amp;lt;node type="A"&gt;&lt;br /&gt;    &amp;lt;this/&gt; &lt;br /&gt;  &amp;lt;/node&gt;&lt;br /&gt;  &amp;lt;node type="B"&gt;&lt;br /&gt;    &amp;lt;that/&gt; &lt;br /&gt;  &amp;lt;/node&gt;&lt;br /&gt;&amp;lt;/root&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Here's the schema:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&amp;lt;xs:schema &lt;br /&gt;  xmlns:xs="http://www.w3.org/2001/XMLSchema" &lt;br /&gt;  elementFormDefault="qualified"&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xs:element name="root" type="root"/&gt;&lt;br /&gt;  &lt;b&gt;&amp;lt;xs:element name="node" type="node"&gt;&lt;br /&gt;    &amp;lt;xs:alternative type="node-type-A" test="@type = 'A'"/&gt;  &lt;br /&gt;    &amp;lt;xs:alternative type="node-type-B" test="@type = 'B'"/&gt;  &lt;br /&gt;  &amp;lt;/xs:element&gt;&lt;/b&gt;&lt;br /&gt;  &amp;lt;xs:element name="this"/&gt;&lt;br /&gt;  &amp;lt;xs:element name="that"/&gt;&lt;br /&gt;  &lt;br /&gt;  &amp;lt;xs:complexType name="root"&gt;&lt;br /&gt;    &amp;lt;xs:sequence&gt;&lt;br /&gt;      &amp;lt;xs:element ref="node" maxOccurs="unbounded"/&gt;&lt;br /&gt;    &amp;lt;/xs:sequence&gt;&lt;br /&gt;  &amp;lt;/xs:complexType&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;b&gt;&amp;lt;-- Base type --&gt;&lt;/b&gt;&lt;br /&gt;  &amp;lt;xs:complexType name="node"&gt;&lt;br /&gt;    &amp;lt;xs:sequence&gt;&lt;br /&gt;      &amp;lt;xs:any/&gt;  &lt;br /&gt;    &amp;lt;/xs:sequence&gt;&lt;br /&gt;    &amp;lt;xs:attribute name="type" type="allowed-node-types"/&gt;&lt;br /&gt;  &amp;lt;/xs:complexType&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xs:simpleType name="allowed-node-types"&gt;&lt;br /&gt;    &amp;lt;xs:restriction base="xs:string"&gt;&lt;br /&gt;      &amp;lt;xs:enumeration value="A"/&gt;&lt;br /&gt;      &amp;lt;xs:enumeration value="B"/&gt;&lt;br /&gt;    &amp;lt;/xs:restriction&gt;&lt;br /&gt;  &amp;lt;/xs:simpleType&gt;      &lt;br /&gt;  &lt;br /&gt;  &lt;b&gt;&amp;lt;-- Type A --&gt;&lt;/b&gt;&lt;br /&gt;  &amp;lt;xs:complexType name="node-type-A"&gt;&lt;br /&gt;    &amp;lt;xs:complexContent&gt;&lt;br /&gt;      &amp;lt;xs:restriction base="node"&gt;&lt;br /&gt;        &amp;lt;xs:sequence&gt;&lt;br /&gt;          &amp;lt;xs:element ref="this"/&gt;  &lt;br /&gt;        &amp;lt;/xs:sequence&gt;  &lt;br /&gt;      &amp;lt;/xs:restriction&gt;  &lt;br /&gt;    &amp;lt;/xs:complexContent&gt;&lt;br /&gt;  &amp;lt;/xs:complexType&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;b&gt;&amp;lt;-- Type B --&gt;&lt;/b&gt;&lt;br /&gt;  &amp;lt;xs:complexType name="node-type-B"&gt;&lt;br /&gt;    &amp;lt;xs:complexContent&gt;&lt;br /&gt;      &amp;lt;xs:restriction base="node"&gt;&lt;br /&gt;        &amp;lt;xs:sequence&gt;&lt;br /&gt;          &amp;lt;xs:element ref="that"/&gt;  &lt;br /&gt;        &amp;lt;/xs:sequence&gt;  &lt;br /&gt;      &amp;lt;/xs:restriction&gt;  &lt;br /&gt;    &amp;lt;/xs:complexContent&gt;&lt;br /&gt;  &amp;lt;/xs:complexType&gt;  &lt;br /&gt;&amp;lt;/xs:schema&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;I really like this... schema 1.1 will be a joy to use.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2760403851453168673?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2760403851453168673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2760403851453168673&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2760403851453168673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2760403851453168673'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/07/validating-co-constrains-in-xml-schema.html' title='Validating co-constrains in XML Schema 1.1 using xs:alternative'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-3464546988639212155</id><published>2008-06-16T14:45:00.001Z</published><updated>2008-06-16T15:10:07.759Z</updated><title type='text'>XML Schema co-occurrence constraint workaround</title><content type='html'>Here's a potential workaround for the co-constraint problem in XML Schema.&lt;br /&gt;&lt;br /&gt;Given some XML:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;elem type="typeA"&gt;&lt;br /&gt;  &amp;lt;typeA/&gt;&lt;br /&gt;&amp;lt;/elem&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;elem type="typeB"&gt;&lt;br /&gt;  &amp;lt;typeB/&gt;&lt;br /&gt;&amp;lt;/elem&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;...the problem is you can't constrain the contents of &lt;code&gt;&amp;lt;elem&gt;&lt;/code&gt; based on the value of the type attribute.&lt;br /&gt;&lt;br /&gt;You can do it though, if you add an xsi:type attribute to it to explicitly set its type:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;elem type="typeA" xsi:type="elem_typeA"&gt;&lt;br /&gt;  &amp;lt;typeA/&gt;&lt;br /&gt;&amp;lt;/elem&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;elem type="typeB" xsi:type="elem_typeB"&gt;&lt;br /&gt;  &amp;lt;typeB/&gt;&lt;br /&gt;&amp;lt;/elem&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;with suitable type definitions in the schema:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;xs:complexType name="elem_typeA"&gt;&lt;br /&gt;  &amp;lt;xs:sequence&gt;&lt;br /&gt;    &amp;lt;xs:element ref="typeA"/&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;&amp;lt;xs:complexType name="elem_typeB"&gt;&lt;br /&gt;  &amp;lt;xs:sequence&gt;&lt;br /&gt;    &amp;lt;xs:element ref="typeB"/&gt;&lt;br /&gt;    ... &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;...and when the XML is validated the relevant definition will be used.&lt;br /&gt;&lt;br /&gt;This technique is far from ideal as it involves modifying the source, but only in a way which doesn't break it for anyone else.  Given the various options for validating co-constraints, this could well be the most straightforward way (at least until 1.1 comes along).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-3464546988639212155?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/3464546988639212155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=3464546988639212155&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/3464546988639212155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/3464546988639212155'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/06/xml-schema-co-occurrence-constraint.html' title='XML Schema co-occurrence constraint workaround'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-278474760210246690</id><published>2008-04-25T13:48:00.001Z</published><updated>2008-04-27T09:58:49.321Z</updated><title type='text'>The Scrabble Reference</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.mobipocket.com/en/eBooks/eBookDetails.asp?BookID=78536&amp;amp;Origine=3931"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://2.bp.blogspot.com/_ypDtgn03LEU/SBHo2c9BFEI/AAAAAAAAABs/UmKpDZqVxi8/s320/scrabble-cover_small.gif" alt="" id="BLOGGER_PHOTO_ID_5193187867517588546" border="0" /&gt;&lt;/a&gt;The Scrabble Reference is an ebook I've created which allows Scrabble players to easily check if words are legal, to suggest longer words or sub-anagrams given a word and to show what words can be made given some letters (up to 15 letters).&lt;br /&gt;&lt;br /&gt;There are two versions, &lt;a href="http://www.mobipocket.com/en/eBooks/eBookDetails.asp?BookID=78536&amp;Origine=3931"&gt;TWL&lt;/a&gt; and &lt;a href="http://www.mobipocket.com/en/eBooks/eBookDetails.asp?BookID=81063&amp;Origine=3931"&gt;SOWPODS&lt;/a&gt; (TWL is used in the USA, Canada, Thailand and Israel and SOWPODS in the rest of the world)&lt;br /&gt;&lt;br /&gt;The ebook is in the Mobipocket format which deals with running the ebooks on all devices - PDAs, mobile phones, Blackberries etc so you just need to transform the input to suitable Mobipocket markup, compile the ebook and their client does the rest.&lt;br /&gt;&lt;br /&gt;I must admit to not being interested in Scrabble, but of course I am interested in XSLT, and given the list of words allowed in Scrabble I thought I should turn them into a product - one that you can run on your phone seems perfect.&lt;br /&gt;&lt;br /&gt;Generating the ebook was very straightforward - list of strings in, markup out...  XSLT 2.0 is ideal for the task.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-278474760210246690?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.mobipocket.com/en/eBooks/eBookDetails.asp?BookID=78536&amp;Origine=3931' title='The Scrabble Reference'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/278474760210246690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=278474760210246690&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/278474760210246690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/278474760210246690'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/04/scrabble-reference.html' title='The Scrabble Reference'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_ypDtgn03LEU/SBHo2c9BFEI/AAAAAAAAABs/UmKpDZqVxi8/s72-c/scrabble-cover_small.gif' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-8905548350216111389</id><published>2008-04-25T11:18:00.000Z</published><updated>2008-04-25T11:45:32.892Z</updated><title type='text'>Relative paths and the document() function</title><content type='html'>A nice gotcha cropped up today on xsl-list...&lt;br /&gt;&lt;br /&gt;Relative paths passed to the document() function are resolved against either the XML or  the stylesheet depending on what is passed in: a node from the XML will mean the path is resolved against the XML, a string will mean it's resolved against the stylesheet.&lt;br /&gt;&lt;br /&gt;The gotcha is this - if you modify this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;document(@path)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;to this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;xsl:variable name="path" select="@path" as="xs:string"/&gt;&lt;br /&gt;...&lt;br /&gt;document($path)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and &lt;code&gt;@path&lt;/code&gt; contains a relative path, then you could get a document not found error,  or worse if your XML and XSLT are in the same directory, you won't notice...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-8905548350216111389?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/8905548350216111389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=8905548350216111389&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/8905548350216111389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/8905548350216111389'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/04/relative-paths-and-document-function.html' title='Relative paths and the document() function'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2588639905897464955</id><published>2008-02-29T16:23:00.001Z</published><updated>2008-02-29T16:34:31.525Z</updated><title type='text'>XML Schema - element with text and attributes</title><content type='html'>For some reason I always forget how to define an element that contains only text but also has attributes.  Perhaps it's because it's so verbose, or so non-intuitive for something so simple, who knows.  Either way it's something that needs to be committed to memory...&lt;br /&gt;&lt;br /&gt;So the element:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;foo bar="bar" baz="baz"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;is described using:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;xs:complexType name="foo"&gt;&lt;br /&gt;    &amp;lt;xs:simpleContent&gt;&lt;br /&gt;        &amp;lt;xs:extension base="xs:string"&gt;&lt;br /&gt;            &amp;lt;xs:attribute name="bar" type="xs:string"/&gt;&lt;br /&gt;            &amp;lt;xs:attribute name="baz" type="xs:string"/&gt;&lt;br /&gt;        &amp;lt;/xs:extension&gt;&lt;br /&gt;    &amp;lt;/xs:simpleContent&gt;                &lt;br /&gt;&amp;lt;/xs:complexType&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;nice!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2588639905897464955?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2588639905897464955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2588639905897464955&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2588639905897464955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2588639905897464955'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/02/xml-schema-element-with-text-and.html' title='XML Schema - element with text and attributes'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2251290802737929380</id><published>2008-02-27T16:10:00.000Z</published><updated>2008-02-27T16:17:34.931Z</updated><title type='text'>schema-aware.com</title><content type='html'>I've created a new website &lt;a href="http://schema-aware.com"&gt;schema-aware.com&lt;/a&gt; which is inteded to contain lots of examples of schema-aware XSLT and XQuery.  I've started it off with half a dozen or so and hope to add more as time goes on.&lt;br /&gt;&lt;br /&gt;I also intend to add a few articles about schema-aware transforms - how the run them from the command line, from Java, the various flags involved, how to write schemas to allow you to use the types in your XSLT etc...  My intentions are good, we'll have to see how much I actually do.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2251290802737929380?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://schema-aware.com' title='schema-aware.com'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2251290802737929380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2251290802737929380&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2251290802737929380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2251290802737929380'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/02/schema-awarecom.html' title='schema-aware.com'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-5584053331799510270</id><published>2008-01-31T15:01:00.000Z</published><updated>2008-01-31T15:20:27.642Z</updated><title type='text'>Kernow 1.6 beta</title><content type='html'>I've uploaded a new version of Kernow (1.6) which contains the rather nice "XSLT Sandbox" tab.  This tab has the XML pane on the left, the XSLT pane on the right and a transform button... and that's it.  It's intended for anyone who wants to quickly try something out without the hassle of files, the command line or starting up a proper IDE.  It does error checking as you type and highlights any problems.&lt;br /&gt;&lt;br /&gt;It's available as the usual download from Sourceforge, or through Java Web Start.  If you already run the JWS version it should automatically update itself (any problems just re-install it).  I've finally figured out the temperamental errors with the JWS version - it turns out the ant jars included with Kernow were already signed by a previous version and so weren't being signed again, but because they were marked as "lazy" in the jnlp the JWS version would start anyway.  (You can tell if a jar has been signed by looking for *.SF and *.DSA in the META-INF directory.)&lt;br /&gt;&lt;br /&gt;The other improvement I'm pleased to have sorted out is that kernow.config (where all of the settings and combobox history are saved) is now stored in a directory called .kernow in your user.home (which is one up from My Documents in XP). Previously it would've been stored on the deskop for the JWS version which is really annoying - sorry about that.  As usual it was a 10 minute job, but just took a while to get around to.&lt;br /&gt;&lt;br /&gt;I've also separated out the SOAP and eXists extension functions into a separate package, so there's no longer the need for the largish eXist.jar, xmldb.jar and log4j.jar jars to be part of the download.&lt;br /&gt;&lt;br /&gt;I'll release a non-beta version in a few weeks if no bugs are reported, and I've got around to updating all of the documentation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-5584053331799510270?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://kernowforsaxon.sf.net/' title='Kernow 1.6 beta'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/5584053331799510270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=5584053331799510270&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/5584053331799510270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/5584053331799510270'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/01/kernow-16-beta.html' title='Kernow 1.6 beta'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-5566597608500397751</id><published>2008-01-31T14:52:00.000Z</published><updated>2008-01-31T14:59:25.322Z</updated><title type='text'>Parsing XML into Java 5 Enums</title><content type='html'>Often when parsing XML into pojos I just resort to writing my own SAX based parser.  It can be long winded but I think gives you the greatest flexibility and control over how you get from the XML to objects you can process.&lt;br /&gt;&lt;br /&gt;One example is with Java 5's Enums, which are great.  Given the kind of XML fragment where currencies are represented using internal codes:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;currency refid="001"/&gt; &amp;lt;!-- 001 is Sterling --&gt;&lt;br /&gt;&amp;lt;currency refid="002"/&gt; &amp;lt;!-- 002 is Euros --&gt;&lt;br /&gt;&amp;lt;currency refid="003"/&gt; &amp;lt;!-- 003 is United States Dollars --&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You can represent each currency element with an Enum, which contains extra fields for the additional information:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public enum Currency {&lt;br /&gt;&lt;br /&gt;    GBP ("001", "GBP", "Sterling"),&lt;br /&gt;    USD ("002", "EUR", "Euros"),&lt;br /&gt;    USD ("003", "USD", "United States Dollar");    &lt;br /&gt;&lt;br /&gt;    private final String refId;&lt;br /&gt;    private final String code;&lt;br /&gt;    private final String desc;&lt;br /&gt;&lt;br /&gt;    Currency(String refId, String code, String desc) {&lt;br /&gt;        this.refId = refId;&lt;br /&gt;        this.code = code;&lt;br /&gt;        this.desc = desc;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String refId() {&lt;br /&gt;        return refId;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String code() {&lt;br /&gt;        return code;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String desc() {&lt;br /&gt;        return desc;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Returns the enum based on it's property rather than its name&lt;br /&gt;    // (This loop could possibly be replaced with a static map, but be aware&lt;br /&gt;    //  that static member variables are initialized *after* the enum and therefore&lt;br /&gt;    //  aren't available to the constructor, so you'd need a static block.  &lt;br /&gt;    public static Currency getTypeByRefId(String refId) {&lt;br /&gt;        for (Currency type : Currency.values()) {&lt;br /&gt;            if (type.refId().equals(refId)) {&lt;br /&gt;                return type;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        throw new IllegalArgumentException("Don't have enum for: " + refId);&lt;br /&gt;    }    &lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice how each enum calls its own contructor with the 3 parameters - the refId, the code, and the description.  &lt;br /&gt;&lt;br /&gt;You parse the XML into the enum by calling &lt;code&gt;Currency.getTypeByRefId(String refId)&lt;/code&gt; passing in the @refid from the XML.  The benefit of using the Enum is that you can then do things like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;if (currency.equals(Currency.GBP))&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;which is nice and clear, while at the same time being able to call &lt;code&gt;currency.refId()&lt;/code&gt; and &lt;code&gt;currency.desc()&lt;/code&gt; to get to the other values.  &lt;br /&gt;&lt;br /&gt;The drawback is that because static member variables are initialized after the enum, you can't create a HashMap and fill it for a faster lookup later (unless you use a static block).  Instead you have to loop through all known values() for the enum given a refId.  Although it feels wrong to loop, the worst case is only the size the of enum so I don't think it's too bad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-5566597608500397751?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/5566597608500397751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=5566597608500397751&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/5566597608500397751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/5566597608500397751'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/01/parsing-xml-into-java-5-enums.html' title='Parsing XML into Java 5 Enums'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2647632193311076501</id><published>2008-01-22T14:06:00.000Z</published><updated>2008-01-22T19:21:41.485Z</updated><title type='text'>Portability of a stylesheet across schema-aware and non-schema-aware processors</title><content type='html'>I came across this today, which I thought was really cool and worth a post.  It basically allows you to code a transform that is only schema-aware if a schema-aware processor is running it, otherwise it's just a standard transform.&lt;br /&gt;&lt;br /&gt;In this case I want to do input and output validation, so first I sort out the schemas:&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:import-schema schema-location="input.xsd" &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;namespace="http://www.foo.com" &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;use-when="system-property('xsl:is-schema-aware')='yes'"/&gt;&lt;br /&gt;  &lt;br /&gt;&amp;lt;xsl:import-schema schema-location="output.xsd"  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;use-when="system-property('xsl:is-schema-aware')='yes'"/&gt;  &lt;br /&gt;&lt;br /&gt;Note the use-when...&lt;br /&gt;&lt;br /&gt;Next define two root matching templates, one for schema-aware, one for basic:&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="/" &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;use-when="system-property('xsl:is-schema-aware')='yes'" &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;priority="2"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:variable name="input" as="document-node()"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:document validation="strict"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:copy-of select="/"/&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;/xsl:document&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;/xsl:variable&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:result-document validation="strict"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:apply-templates select="$input/the-root-elem"/&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;/xsl:result-document&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="/"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:apply-templates select="the-root-elem"/&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;br /&gt;&amp;lt;xsl:template match="the-root-elem"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;...&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;The root matching template for schema-aware processing uses xsl:document to validate the input, and xsl:result-document to validate the output.  Validation can also be controlled from outside the transform, but this way forces it on.&lt;br /&gt;&lt;br /&gt;I think this is great :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2647632193311076501?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2647632193311076501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2647632193311076501&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2647632193311076501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2647632193311076501'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/01/portability-of-stylesheet-across-schema.html' title='Portability of a stylesheet across schema-aware and non-schema-aware processors'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2855377856737708141</id><published>2008-01-22T13:47:00.001Z</published><updated>2009-07-05T08:06:58.303Z</updated><title type='text'>The identity transform for XSLT 2.0</title><content type='html'>I was looking at the standard identity transform the other day and realised that for nodes other than elements, the call to apply-templates is redundant. &lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="@*|node()"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;xsl:copy&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:apply-templates select="@*|node()"/&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;/xsl:copy&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;Also, although it might be intuitive to think that attributes have separate nodes for their name and value, they are in fact a single node that's copied in it's entirety by xsl:copy.&lt;br /&gt;&lt;br /&gt;I raised this on xsl-list and suggested seperating out the attribute into a template of its own with just xsl:copy for its body:&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="node()"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;xsl:copy&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:apply-templates select="@*|node()"/&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;/xsl:copy&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="@*"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;xsl:copy/&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Mike Kay suggested a more logical version would be:&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="element()"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;xsl:copy&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;lt;xsl:apply-templates select="@*,node()"/&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;lt;/xsl:copy&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="attribute()|text()|comment()|processing-instruction()"&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;xsl:copy/&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;This turned out to be ideal for three reasons:&lt;br /&gt;&lt;br /&gt;- the comma between @* and node() will mean the selected nodes will be processed in that order, removing the sorting and deduplication that takes place with union |&lt;br /&gt;- apply-templates is only called when it will have an effect&lt;br /&gt;- it's clearer that attributes are leaf nodes&lt;br /&gt;&lt;br /&gt;So there it is... the identity transform for XSLT 2.0&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2855377856737708141?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2855377856737708141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2855377856737708141&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2855377856737708141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2855377856737708141'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2008/01/indentity-transform-for-xslt-20.html' title='The identity transform for XSLT 2.0'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-345186737016129299</id><published>2007-09-28T13:04:00.000Z</published><updated>2007-09-28T13:07:52.202Z</updated><title type='text'>Kernow 1.5.2</title><content type='html'>I've just uploaded the non-beta version of Kernow 1.5.2.&lt;br /&gt;&lt;br /&gt;This version contains:&lt;br /&gt;&lt;br /&gt;- French and German translations  &lt;br /&gt;- XQuery syntax highlighting and checking as-you-type &lt;br /&gt;- Improved cancelling of Single File and Standalone tasks &lt;br /&gt;- icon and splash screen &lt;br /&gt;- An exe to launch it (for windows users)  &lt;br /&gt;- context menus  &lt;br /&gt;- comboboxes remember their selected index  &lt;br /&gt;- individual combobox entries can be removed by deleting the entry  &lt;br /&gt;- other small fixes&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-345186737016129299?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://kernowforsaxon.sourceforge.net/' title='Kernow 1.5.2'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/345186737016129299/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=345186737016129299&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/345186737016129299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/345186737016129299'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/09/kernow-152.html' title='Kernow 1.5.2'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-8883359722728443102</id><published>2007-09-12T12:01:00.000Z</published><updated>2007-09-12T12:34:31.817Z</updated><title type='text'>Connecting to Oracle from XSLT</title><content type='html'>Today I generated a report by connecting directly to an Oracle database from XSLT, and thought I'd share the basic stylesheet.  I used &lt;a href="http://www.saxonica.com/documentation/sql-extension/intro.html"&gt;Saxon's SQL extension&lt;/a&gt;, which is available when saxon8-sql.jar is on the classpath.  As I was connecting to Oracle, I also needed to put ojdcb14.jar on the classpath.&lt;br /&gt;&lt;br /&gt;Here's the stylesheet in it's most basic form, formatted for display in this blog.&lt;br /&gt;&lt;br /&gt;The important things to note here are:&lt;br /&gt;&lt;br /&gt;- The sql prefix is bound to "&lt;code&gt;/net.sf.saxon.sql.SQLElementFactory&lt;/code&gt;"&lt;br /&gt;- The driver is "&lt;code&gt;oracle.jdbc.driver.OracleDriver&lt;/code&gt;"&lt;br /&gt;- The connection string format is "&lt;code&gt;jdbc:oracle:thin:@1.2.3.4:1234:sid&lt;/code&gt;" (note the colon between thin and @ - I missed that first time round) where the IP, port and sid  are placeholders for the real values&lt;br /&gt;- remember that &lt;code&gt;saxon8-sql.jar&lt;/code&gt; and &lt;code&gt;ojdbc14.jar&lt;/code&gt; needed to be on the classpath&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;xsl:stylesheet version="2.0"&lt;br /&gt; xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&lt;br /&gt; xmlns:xs="http://www.w3.org/2001/XMLSchema"&lt;br /&gt; xmlns:sql="/net.sf.saxon.sql.SQLElementFactory"&lt;br /&gt; exclude-result-prefixes="xs"&lt;br /&gt; extension-element-prefixes="sql"&gt;&lt;br /&gt; &lt;br /&gt;&amp;lt;xsl:output indent="yes"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:param name="driver" &lt;br /&gt;  select="'oracle.jdbc.driver.OracleDriver'" &lt;br /&gt;  as="xs:string"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:param name="database" &lt;br /&gt;  select="'jdbc:oracle:thin:@123.123.123.123:1234:sid'" &lt;br /&gt;  as="xs:string"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:param name="user" select="'un'" as="xs:string"/&gt;&lt;br /&gt;&amp;lt;xsl:param name="password" select="'pw'" as="xs:string"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:variable name="connection"&lt;br /&gt;  as="java:java.sql.Connection" &lt;br /&gt;  xmlns:java="http://saxon.sf.net/java-type"&gt;&lt;br /&gt; &lt;br /&gt;  &amp;lt;sql:connect driver="{$driver}" database="{$database}" &lt;br /&gt;    user="{$user}" password="{$password}"/&gt;&lt;br /&gt;&amp;lt;/xsl:variable&gt;&lt;br /&gt; &lt;br /&gt;&amp;lt;xsl:template match="/" name="main"&gt;&lt;br /&gt;  &amp;lt;root&gt;&lt;br /&gt;    &amp;lt;sql:query connection="$connection" &lt;br /&gt;      table="some_table" &lt;br /&gt;      column="*"&lt;br /&gt;      row-tag="row" &lt;br /&gt;      column-tag="col"/&gt;&lt;br /&gt;  &amp;lt;/root&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/xsl:stylesheet&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;The result of this transform outputs XML in the form:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;root&gt;&lt;br /&gt;  &amp;lt;row&gt;&lt;br /&gt;    &amp;lt;col&gt;data1&amp;lt;/col&gt;&lt;br /&gt;    &amp;lt;col&gt;data2&amp;lt;/col&gt;&lt;br /&gt;    &amp;lt;col&gt;data3&amp;lt;/col&gt;&lt;br /&gt;    &amp;lt;col&gt;data4&amp;lt;/col&gt;&lt;br /&gt;  &amp;lt;/row&gt;&lt;br /&gt;  ....&lt;br /&gt;&amp;lt;/root&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;where &lt;code&gt;&amp;lt;root&gt;&lt;/code&gt; is the wrapper element, and &lt;code&gt;&amp;lt;row&gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;col&gt;&lt;/code&gt; are the element names specified in the &lt;code&gt;&amp;lt;sql:query&gt;&lt;/code&gt; element.&lt;br /&gt;&lt;br /&gt;And that's it - connecting to an Oracle database from within XSLT.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-8883359722728443102?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/8883359722728443102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=8883359722728443102&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/8883359722728443102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/8883359722728443102'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/09/connecting-to-oracle-from-xslt.html' title='Connecting to Oracle from XSLT'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-158959017110751435</id><published>2007-09-03T15:59:00.000Z</published><updated>2007-09-03T16:29:31.326Z</updated><title type='text'>Kernow 1.5.2 beta b2 available</title><content type='html'>I've just uploaded a new version of Kernow.  This one was pretty much already available via Java Web Start,  this makes it available via the normal download route.&lt;br /&gt;&lt;br /&gt;New features/fixes:&lt;br /&gt;&lt;br /&gt;- Added syntax highlighting and checking as-you-type to the XQuery Sandbox tab. Syntax highlighting's provided using Bounce's XMLEditorKit - I'm hoping to use Netbeans' nbEditorKit in a future version which will add line numbers, code completion etc.  I've put together the checking-as-you-type and error highlighting using Saxon's error reporting.  This is really cool, so I'm planning on doing an equivalent "XSLT Sandbox" soon... perhaps using Netbeans RCP.  Not sure yet.&lt;br /&gt;&lt;br /&gt;- Added an icon and splashsceen.  These came about because JWS and the exe benefit from them.  Are they any good?  I'm not really a graphics person...&lt;br /&gt;&lt;br /&gt;- Kernow.jar is now a proper executable jar, so you can double click it to run Kernow (if you're on a mac for example)&lt;br /&gt;&lt;br /&gt;- It's all compiled using Java 1.5, again for mac users where 1.6 isn't supported yet.&lt;br /&gt;&lt;br /&gt;It's available here: &lt;a href="http://sourceforge.net/projects/kernowforsaxon"&gt;Kernow&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-158959017110751435?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/158959017110751435/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=158959017110751435&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/158959017110751435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/158959017110751435'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/09/kernow-152-beta-b2-available.html' title='Kernow 1.5.2 beta b2 available'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-7912423256246298979</id><published>2007-08-30T15:16:00.000Z</published><updated>2007-08-30T15:30:11.533Z</updated><title type='text'>Kernow now available via Java Web Start</title><content type='html'>I've been playing around with making Kernow available through Java Web Start.  This should be the ideal way to run Kernow as it places a shortcut on your desktop (and in your start menu in Windows) and auto-updates whenever a new version is available.&lt;br /&gt;&lt;br /&gt;Reading around it seems Java Web Start has had mixed reviews.  Personally I really like it, perhaps because I'm using Java 1.6 and Netbeans 6 M10 which makes it all pretty straightforward (auto jar-signing is really helpful in M10).&lt;br /&gt;&lt;br /&gt;Give it a go, let me know what you think: &lt;a href="http://kernowforsaxon.sourceforge.net/jws.html"&gt;Kernow - Java Web Start&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-7912423256246298979?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://kernowforsaxon.sourceforge.net/jws.html' title='Kernow now available via Java Web Start'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/7912423256246298979/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=7912423256246298979&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7912423256246298979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7912423256246298979'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/08/kernow-now-available-via-java-web-start.html' title='Kernow now available via Java Web Start'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-7393500625857233347</id><published>2007-08-17T14:18:00.000Z</published><updated>2007-08-17T14:29:53.411Z</updated><title type='text'>Using XQuery and the slash operator to quickly try out XPaths</title><content type='html'>This is the coolest thing I've seen in a while...  &lt;br /&gt;&lt;br /&gt;In XQuery you can constuct a node just by writing it, eg:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;node&gt;I'm a node&amp;lt;/node&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;and then you can use slash operator to apply an XPath to that node:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;node&gt;I'm a node&amp;lt;/node&gt;/data(.)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;returns "I'm a node"&lt;br /&gt;&lt;br /&gt;The XML doesn't have to be limited to a single node - you can do:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;root&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;node&gt;foo&amp;lt;/node&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;node&gt;bar&amp;lt;/node&gt;&lt;br /&gt;&amp;lt;/root&gt;/node/data(.)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;...to get "foo bar".&lt;br /&gt;&lt;br /&gt;Or:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;root&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;node&gt;foo&amp;lt;/node&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;lt;node&gt;bar&amp;lt;/node&gt;&lt;br /&gt;&amp;lt;/root&gt;/node[1]&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to get:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;node&gt;foo&amp;lt;/node&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Using this technique in combination with &lt;a href="http://kernowforsaxon.sf.net/"&gt;Kernow's XQuery Sandbox&lt;/a&gt; makes it straightforward to paste in some XML and start trying out some XPaths.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-7393500625857233347?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/7393500625857233347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=7393500625857233347&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7393500625857233347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/7393500625857233347'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/08/using-xquery-and-slash-operator-to.html' title='Using XQuery and the slash operator to quickly try out XPaths'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-6665979842412983609</id><published>2007-08-16T14:59:00.000Z</published><updated>2007-08-16T15:44:44.282Z</updated><title type='text'>When a = b and a != b both return true...</title><content type='html'>In XPath = and != are set operators.  That is, they return true if any item on the left hand side returns true when compared with any item on the right hand side.  Or in other words:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;some x in $seqA, y in $seqB satisfies x op y&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;...where "op" is = or != (or &gt; or &amp;lt; etc)&lt;br /&gt;&lt;br /&gt;To demonstrate this take the two sets ('a', 'b') and ('b', 'c'):&lt;br /&gt;&lt;br /&gt;$seqA = $seqB returns true because both sets contains 'b'&lt;br /&gt;&lt;br /&gt;$seqA != $seqB returns true because setA contains 'a' which is not equal to 'c' in setB&lt;br /&gt;&lt;br /&gt;This catches me out a lot, even though I've been caught out before several times.  I really have to think hard about what it is exactly that I'm comparing, and still end up getting it wrong.&lt;br /&gt;&lt;br /&gt;A simple rules to follow is &lt;b&gt;"never use != where both sides are sequences of more than one item"&lt;/b&gt;.  99.9% of the time you won't need to, as much as it feels like the right thing to do.  &lt;br /&gt;&lt;br /&gt;Below are some of the most common operations on sequences, put together for a reference.&lt;br /&gt;&lt;br /&gt;The two sequences are ('a', 'b') and ('b', 'c'), which can be defined in XSLT as:&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:variable name="seqA" select="('a', 'b')" as="xs:string+"/&gt;&lt;br /&gt;&amp;lt;xsl:variable name="seqB" select="('b', 'c')" as="xs:string+"/&gt;&lt;br /&gt;&lt;br /&gt;or in XQuery as:&lt;br /&gt;&lt;br /&gt;let $seqA := ('a', 'b')&lt;br /&gt;let $seqB := ('b', 'c')&lt;br /&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;  &lt;div&gt;&lt;i&gt;Select all items in both sequences&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;($seqA, $seqB)&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;Result: a b b c&lt;/div&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;  &lt;div&gt;&lt;i&gt;Select all items in both sequences, eliminating duplicates&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;distinct-values(($seqA, $seqB))&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;Result: a b c&lt;/div&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;  &lt;div&gt;&lt;i&gt;Select all items that occur in $seq1 but not $seq2&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;$seqA[not(. = $seqB)]&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;Result: a&lt;/div&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;  &lt;div&gt;&lt;i&gt;Select all items that occur in both sequences&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;$seqA[. = $seqB]&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;Result: b&lt;/div&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;  &lt;div&gt;&lt;i&gt;Select all items that do not occur in both sequences&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;($seqA[not(. = $seqB)],$seqB[not(. = $seqA)])&lt;br /&gt;            &lt;br&gt;or&lt;br&gt;&lt;br /&gt;            ($seqA, $seqB)[not(. = $seqA[. = $seqB])]&lt;br /&gt;          &lt;br /&gt;  &lt;/div&gt;&lt;br /&gt;  &lt;div&gt;Result: a c&lt;/div&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;  &lt;div&gt;&lt;i&gt;Determine if both sequences are identical&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;deep-equal($seqA, $seqB)&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;Result: false&lt;/div&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;  &lt;div&gt;&lt;i&gt;Test if all items in the sequence are different&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;count(distinct-values($seqA)) eq count($seqA)&lt;/div&gt;&lt;br /&gt;  &lt;div&gt;Result: true&lt;/div&gt;&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-6665979842412983609?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/6665979842412983609/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=6665979842412983609&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/6665979842412983609'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/6665979842412983609'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/08/when-b-and-b-both-return-true.html' title='When a = b and a != b both return true...'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2939328824060159474</id><published>2007-08-15T09:24:00.000Z</published><updated>2007-08-15T10:31:08.553Z</updated><title type='text'>The Worlds Fastest Sudoku Solution in XSLT 2.0 for the Worlds Hardest Sudoku - Al Escargot</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ypDtgn03LEU/RsLIJuCKmpI/AAAAAAAAABE/GsS_ZPWtXdE/s1600-h/Al+escargot.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://1.bp.blogspot.com/_ypDtgn03LEU/RsLIJuCKmpI/AAAAAAAAABE/GsS_ZPWtXdE/s320/Al+escargot.jpg" alt="" id="BLOGGER_PHOTO_ID_5098857797438315154" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I get a lot of traffic to this blog because of my Sudoku solver.  Google analytics tells me most of it lands on the &lt;a href="http://ajwelch.blogspot.com/2006/05/sudoku-solver-complete-and-shes-fast_02.html"&gt;original version&lt;/a&gt; that I wrote, and not the &lt;a href="http://www.andrewjwelch.com/code/xslt/sudoku/sudoku-solver.html"&gt;optimised version&lt;/a&gt; that's now the worlds fastest XSLT 2.0 solution - an issue which this post should hopefully rectify.&lt;br /&gt;&lt;br /&gt;The puzzle on the right is apparently the &lt;a href="http://zonkedyak.blogspot.com/2006/11/worlds-hardest-sudoku-puzzle-al.html"&gt;worlds hardest Sudoku puzzle&lt;/a&gt;.  If I run my solver "&lt;a href="http://www.andrewjwelch.com/code/xslt/sudoku/sudoku-solver.html"&gt;Sudoku.xslt&lt;/a&gt;" using &lt;a href="http://kernowforsaxon.sourceforge.net/"&gt;Kernow's performance testing feature&lt;/a&gt; I get this result:&lt;br /&gt;&lt;br /&gt;Ran 5 times&lt;br /&gt;Run 1: 328 ms&lt;br /&gt;Run 2: 344 ms&lt;br /&gt;Run 3: 328 ms&lt;br /&gt;Run 4: 391 ms&lt;br /&gt;Run 5: 328 ms&lt;br /&gt;Ignoring first 2 times&lt;br /&gt;Total Time (last 3 runs): 1 second 47 ms&lt;br /&gt;Average Time (last 3 runs): 349 ms&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So on my machine the average execution time is 349ms, which is pretty good considering the original version would take minutes for several puzzles.  As far as I know this version will solve all puzzles in under a second on my machine (Core 2 duo E6600, 2gb).&lt;br /&gt;&lt;br /&gt;How does it do it?  This is taken from the &lt;a href="http://www.andrewjwelch.com/code/xslt/sudoku/sudoku-solver.html"&gt;web page&lt;/a&gt; where it's hosted:&lt;br /&gt;&lt;br /&gt;It accepts the puzzle as 81 comma separated integers in the range 0 to 9, with zero representing empty.  It works by continuously reducing the number of possible values for each cell, and only when the possible values can't be reduced any further it starts backtracking.&lt;br /&gt;&lt;br /&gt;The first phase attempts to populate as many cells of the board based on the start values. For each empty cell it works out the possible values using the "Naked Single", "Hidden Single" and "Naked Tuples" techniques in that order (read here for more on the techniques). Cells where only one possible value exists are populated and then the second phase begins.&lt;br /&gt;&lt;br /&gt;The second phase follows this process:&lt;br /&gt;&lt;br /&gt;   * Find all empty cells and get all the possible values for each cell (using Naked Single and Hidden Single techniques)&lt;br /&gt;   * Sort the cells by least possible values first&lt;br /&gt;   * Populate the cells with only one possible value&lt;br /&gt;   * If more there's more than one value, go through them one by one&lt;br /&gt;   * Repeat&lt;br /&gt;&lt;br /&gt;This is how it solves the Al Esgargot: A slightly modified version of the solution gives this output with the $verbose parameter set to true.  As you can see it's found that it can insert a 1 at position 66 using the static analysis of the puzzle (position 66 is middle-right of the bottom-left group).  Next it's decided that there are two possible values 4 and 6 at index 12 (middle-right cell of top-left group), so it tries 4 and continues.  With that 4 in place it's found that there's only one possible value at index 39, a 3, so it inserts that and continues.  It will keep reducing the possible values based on the current state of the board, inserting the only possible values or trying each one when there are many, until either there are no possible values for an empty cell, or the puzzle is solved.&lt;br /&gt;&lt;br /&gt;(the solution is shown below)&lt;br /&gt;&lt;br /&gt;Populated single value cell at index 66 with 1&lt;br /&gt;Trying 4 out of a possible 4 6 at index 12&lt;br /&gt;Only one value 3 for index 39&lt;br /&gt;Trying 5 out of a possible 5 7 at index 10&lt;br /&gt;Trying 1 out of a possible 1 9 at index 13&lt;br /&gt;Only one value 9 for index 15&lt;br /&gt;Trying 6 out of a possible 6 7 at index 16&lt;br /&gt;Only one value 7 for index 17&lt;br /&gt;Trying 2 out of a possible 2 4 at index 7&lt;br /&gt;Trying 6 out of a possible 6 8 at index 2&lt;br /&gt;Only one value 8 for index 3&lt;br /&gt;Only one value 2 for index 48&lt;br /&gt;Only one value 6 for index 57&lt;br /&gt;Only one value 5 for index 60&lt;br /&gt;Only one value 9 for index 63&lt;br /&gt;Only one value 5 for index 74&lt;br /&gt;Only one value 1 for index 78&lt;br /&gt;Only one value 3 for index 69&lt;br /&gt;Only one value 5 for index 71&lt;br /&gt;Only one value 6 for index 81&lt;br /&gt;Only one value 4 for index 36&lt;br /&gt;! Cannot go any further !&lt;br /&gt;Trying 8 out of a possible 8 at index 2&lt;br /&gt;Only one value 6 for index 3&lt;br /&gt;Trying 4 out of a possible 4 5 at index 4&lt;br /&gt;Only one value 3 for index 9&lt;br /&gt;Only one value 3 for index 23&lt;br /&gt;Only one value 4 for index 26&lt;br /&gt;Only one value 3 for index 53&lt;br /&gt;Only one value 5 for index 54&lt;br /&gt;Only one value 4 for index 36&lt;br /&gt;Only one value 6 for index 44&lt;br /&gt;Only one value 8 for index 48&lt;br /&gt;Only one value 2 for index 57&lt;br /&gt;Only one value 8 for index 73&lt;br /&gt;Only one value 9 for index 47&lt;br /&gt;Only one value 7 for index 50&lt;br /&gt;Only one value 6 for index 60&lt;br /&gt;Only one value 9 for index 64&lt;br /&gt;! Cannot go any further !&lt;br /&gt;Trying 5 out of a possible 5 at index 4&lt;br /&gt;Only one value 9 for index 47&lt;br /&gt;Only one value 2 for index 67&lt;br /&gt;Only one value 7 for index 49&lt;br /&gt;Only one value 9 for index 64&lt;br /&gt;Only one value 2 for index 80&lt;br /&gt;Only one value 1 for index 32&lt;br /&gt;Only one value 2 for index 48&lt;br /&gt;Only one value 5 for index 50&lt;br /&gt;Only one value 8 for index 58&lt;br /&gt;Only one value 8 for index 73&lt;br /&gt;! Cannot go any further !&lt;br /&gt;Trying 4 out of a possible 4 at index 7&lt;br /&gt;Only one value 3 for index 9&lt;br /&gt;Only one value 4 for index 23&lt;br /&gt;Only one value 3 for index 24&lt;br /&gt;Only one value 2 for index 26&lt;br /&gt;Only one value 3 for index 53&lt;br /&gt;Only one value 5 for index 54&lt;br /&gt;Only one value 8 for index 4&lt;br /&gt;Trying 2 out of a possible 2 6 at index 2&lt;br /&gt;Only one value 6 for index 3&lt;br /&gt;Trying 7 out of a possible 7 8 at index 19&lt;br /&gt;Only one value 8 for index 20&lt;br /&gt;Only one value 7 for index 29&lt;br /&gt;Only one value 9 for index 40&lt;br /&gt;Only one value 7 for index 50&lt;br /&gt;Only one value 6 for index 44&lt;br /&gt;Only one value 2 for index 49&lt;br /&gt;Only one value 2 for index 28&lt;br /&gt;Only one value 8 for index 48&lt;br /&gt;Only one value 2 for index 57&lt;br /&gt;Only one value 5 for index 67&lt;br /&gt;Only one value 7 for index 58&lt;br /&gt;Only one value 8 for index 61&lt;br /&gt;Only one value 3 for index 68&lt;br /&gt;Only one value 2 for index 70&lt;br /&gt;! Cannot go any further !&lt;br /&gt;Trying 8 out of a possible 8 at index 19&lt;br /&gt;Only one value 7 for index 20&lt;br /&gt;Only one value 8 for index 29&lt;br /&gt;Only one value 9 for index 47&lt;br /&gt;Only one value 2 for index 48&lt;br /&gt;Only one value 8 for index 57&lt;br /&gt;Only one value 4 for index 37&lt;br /&gt;Only one value 9 for index 40&lt;br /&gt;Only one value 7 for index 49&lt;br /&gt;! Cannot go any further !&lt;br /&gt;Trying 6 out of a possible 6 at index 2&lt;br /&gt;Only one value 2 for index 3&lt;br /&gt;Only one value 6 for index 57&lt;br /&gt;Trying 7 out of a possible 7 8 at index 19&lt;br /&gt;Only one value 8 for index 20&lt;br /&gt;Trying 2 out of a possible 2 4 at index 28&lt;br /&gt;Only one value 7 for index 29&lt;br /&gt;Only one value 9 for index 47&lt;br /&gt;Only one value 2 for index 49&lt;br /&gt;Only one value 9 for index 40&lt;br /&gt;Only one value 6 for index 44&lt;br /&gt;Only one value 7 for index 50&lt;br /&gt;Only one value 9 for index 59&lt;br /&gt;! Cannot go any further !&lt;br /&gt;Trying 4 out of a possible 4 at index 28&lt;br /&gt;Only one value 9 for index 37&lt;br /&gt;Only one value 4 for index 44&lt;br /&gt;Only one value 6 for index 42&lt;br /&gt;Trying 2 out of a possible 2 7 at index 29&lt;br /&gt;Only one value 7 for index 47&lt;br /&gt;Only one value 2 for index 49&lt;br /&gt;Only one value 7 for index 32&lt;br /&gt;Only one value 9 for index 50&lt;br /&gt;! Cannot go any further !&lt;br /&gt;Trying 7 out of a possible 7 at index 29&lt;br /&gt;Only one value 1 for index 32&lt;br /&gt;Only one value 2 for index 33&lt;br /&gt;Only one value 2 for index 47&lt;br /&gt;Trying 7 out of a possible 7 9 at index 49&lt;br /&gt;Only one value 9 for index 50&lt;br /&gt;Only one value 6 for index 77&lt;br /&gt;Only one value 1 for index 78&lt;br /&gt;Only one value 5 for index 80&lt;br /&gt;Only one value 5 for index 56&lt;br /&gt;Only one value 8 for index 60&lt;br /&gt;Only one value 2 for index 61&lt;br /&gt;Only one value 8 for index 70&lt;br /&gt;Only one value 6 for index 71&lt;br /&gt;Only one value 9 for index 74&lt;br /&gt;Only one value 2 for index 64&lt;br /&gt;Only one value 4 for index 81&lt;br /&gt;Only one value 4 for index 58&lt;br /&gt;Only one value 9 for index 67&lt;br /&gt;Only one value 8 for index 73&lt;br /&gt;Only one value 2 for index 76&lt;br /&gt;Done!&lt;br /&gt;&lt;br /&gt;1, 6, 2,&amp;#160;&amp;#160;&amp;#160;8, 5, 7,&amp;#160;&amp;#160;&amp;#160;4, 9, 3,  &lt;br /&gt;5, 3, 4,&amp;#160;&amp;#160;&amp;#160;1, 2, 9,&amp;#160;&amp;#160;&amp;#160;6, 7, 8,  &lt;br /&gt;7, 8, 9,&amp;#160;&amp;#160;&amp;#160;6, 4, 3,&amp;#160;&amp;#160;&amp;#160;5, 2, 1,  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4, 7, 5,&amp;#160;&amp;#160;&amp;#160;3, 1, 2,&amp;#160;&amp;#160;&amp;#160;9, 8, 6,  &lt;br /&gt;9, 1, 3,&amp;#160;&amp;#160;&amp;#160;5, 8, 6,&amp;#160;&amp;#160;&amp;#160;7, 4, 2,  &lt;br /&gt;6, 2, 8,&amp;#160;&amp;#160;&amp;#160;7, 9, 4,&amp;#160;&amp;#160;&amp;#160;1, 3, 5,  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3, 5, 6,&amp;#160;&amp;#160;&amp;#160;4, 7, 8,&amp;#160;&amp;#160;&amp;#160;2, 1, 9,  &lt;br /&gt;2, 4, 1,&amp;#160;&amp;#160;&amp;#160;9, 3, 5,&amp;#160;&amp;#160;&amp;#160;8, 6, 7,  &lt;br /&gt;8, 9, 7,&amp;#160;&amp;#160;&amp;#160;2, 6, 1,&amp;#160;&amp;#160;&amp;#160;3, 5, 4,  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If you have a solution that can statically detect more cells to fill using different techniques than I have, or has a better strategy than simply backtracking when there's more than one value, then I'd be interested to know it works. &lt;br /&gt;&lt;br /&gt;I'm pretty sure the XSLT is as good as it can be, but if you think it can be improved  in any way then let me know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2939328824060159474?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://andrewjwelch.com/code/xslt/sudoku/sudoku-solver.html' title='The Worlds Fastest Sudoku Solution in XSLT 2.0 for the Worlds Hardest Sudoku - Al Escargot'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2939328824060159474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2939328824060159474&amp;isPopup=true' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2939328824060159474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2939328824060159474'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/08/worlds-fastest-sudoku-solution-in-xslt.html' title='The Worlds Fastest Sudoku Solution in XSLT 2.0 for the Worlds Hardest Sudoku - Al Escargot'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_ypDtgn03LEU/RsLIJuCKmpI/AAAAAAAAABE/GsS_ZPWtXdE/s72-c/Al+escargot.jpg' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-4554652156325501454</id><published>2007-07-23T10:23:00.000Z</published><updated>2007-07-23T10:40:23.128Z</updated><title type='text'>Combining XSLT 2.0's Grouping with eXist</title><content type='html'>I work a lot with large XML datasets that are arranged as thousands of 1 - 10mb XML files.  I spend most of my days writing transforms and transformation pipelines to process these files, which is where &lt;a href="http://kernowforsaxon.sourceforge.net/"&gt;Kernow&lt;/a&gt; came from.  I also like messing around with &lt;a href="http://exist.sourceforge.net/"&gt;eXist&lt;/a&gt; (I'm yet to use it commercially, but I hope to one day) and enjoying the speed a native XML database gives you.&lt;div id="content"&gt;&lt;div class="entry"&gt;&lt;br /&gt;    &lt;div class="para"&gt;Requirements that regularly come up are to generate indexes and reports for the dataset.  This is nice and simple using XSLT 2.0's grouping but require the whole dataset to be in memory, unless you use &lt;a href="http://ajwelch.blogspot.com/2006/11/using-collection-and-saxondiscard.html"&gt;saxon:discard-document()&lt;/a&gt;.  It can also be quite slow, if only because you have to read GB's from disk and parse the whole of each and every XML input file to just get the snippet that you're interested in (such as the title, or say all of the &lt;xref&gt; elements). &lt;/xref&gt;&lt;/div&gt;&lt;br /&gt;    &lt;div class="para"&gt;Conversely, XQuery doesn't suffer from the dataset size but lacks XSLT 2.0's grouping features.  It's perfectly possible (although a bit involved - you could say "a bit XSLT 1.0") to recreate the grouping in XQuery, but it's just so much nicer in XSLT 2.0.  So to get the best of both, you can use eXist's &lt;a href="http://exist.sourceforge.net/devguide.html#N1021D"&gt;fanstastic REST style interface&lt;/a&gt; to select the parts of the XML you're interested in, and then use XSLT 2.0's for-each-group to arrange the results.&lt;/div&gt;     &lt;br /&gt;   &lt;div class="para"&gt;In the example stylesheet below I create an index by getting the &amp;lt;title&gt; for each XML document, and then grouping the titles by their first letter, then sorting by title itself. I use eXist to get the &amp;lt;title&gt; element, then XSLT 2.0 to do the sorting and grouping.&lt;/div&gt;&lt;br /&gt;  &lt;div class="para"&gt;I have an instance of eXist running on my local machine and fully populated with the XML dataset.  The function fn:eXist() takes the collection I'm interested in and the XQuery to execute against that collection, constructs the correct URI for the REST interface and calls doc() with that URI.  The result is a &lt;a href="http://exist.sourceforge.net/devguide.html#N1029D"&gt;proprietary XML format&lt;/a&gt; containing each tuple that I then group using xsl:for-each-group.  It's worth noting the -1 value for the &lt;code&gt;_howmany&lt;/code&gt; parameter on the query - without this it defaults to 10.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="xmlverb-default"&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;stylesheet&lt;/span&gt;&lt;span class="xmlverb-ns-name"&gt;&lt;br /&gt;&amp;#xa; xmlns:xsl&lt;/span&gt;="&lt;span class="xmlverb-ns-uri"&gt;http://www.w3.org/1999/XSL/Transform&lt;/span&gt;"&lt;span class="xmlverb-ns-name"&gt;&lt;br /&gt;&amp;#xa; xmlns:fn&lt;/span&gt;="&lt;span class="xmlverb-ns-uri"&gt;fn&lt;/span&gt;"&lt;span class="xmlverb-ns-name"&gt;&lt;br /&gt;&amp;#xa; xmlns:exist&lt;/span&gt;="&lt;span class="xmlverb-ns-uri"&gt;http://exist.sourceforge.net/NS/exist&lt;/span&gt;"&lt;br /&gt;&amp;#xa; &lt;span class="xmlverb-attr-name"&gt;version&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;2.0&lt;/span&gt;"&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#xa;&lt;br /&gt;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;output&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;indent&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;yes&lt;/span&gt;" /&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#xa;&lt;br /&gt;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;param&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;name&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;db-uri&lt;/span&gt;" &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;'http://localhost:8080/exist/rest'&lt;/span&gt;" /&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#xa;&lt;br /&gt;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;function&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;name&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;fn:eXist&lt;/span&gt;"&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;param&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;name&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;collection&lt;/span&gt;" /&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;param&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;name&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;query&lt;/span&gt;" /&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;sequence&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;doc(concat($db-uri, $collection, '?_query=', $query, '&amp;amp;amp;_start=1&amp;amp;amp;_howmany=-1'))/exist:result/node()&lt;/span&gt;" /&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&lt;/span&gt;&amp;lt;/&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;function&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#xa;&lt;br /&gt;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;template&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;match&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;/&lt;/span&gt;"&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;for-each-group&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;fn:eXist('/db/mycomp/myproject', '/doc/head/title')&lt;/span&gt;" &lt;span class="xmlverb-attr-name"&gt;group-by&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;substring(., 1, 1)&lt;/span&gt;"&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;sort&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;.&lt;/span&gt;" /&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;value-of&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;current-grouping-key()&lt;/span&gt;" /&amp;gt;&amp;lt;/&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;for-each&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;current-group()&lt;/span&gt;"&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;sort&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;.&lt;/span&gt;" /&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&amp;lt;&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;value-of&lt;/span&gt; &lt;span class="xmlverb-attr-name"&gt;select&lt;/span&gt;="&lt;span class="xmlverb-attr-content"&gt;.&lt;/span&gt;" /&amp;gt;&amp;lt;/&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;/&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;for-each&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;/&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;/&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;for-each-group&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&lt;/span&gt;&amp;lt;/&lt;span class="xmlverb-element-name"&gt;div&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&lt;/span&gt;&amp;lt;/&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;template&lt;/span&gt;&amp;gt;&lt;span class="xmlverb-text"&gt;&amp;#xa;&lt;br /&gt;&amp;#xa;&lt;br /&gt;&lt;/span&gt;&amp;lt;/&lt;span class="xmlverb-element-nsprefix"&gt;xsl&lt;/span&gt;:&lt;span class="xmlverb-element-name"&gt;stylesheet&lt;/span&gt;&amp;gt;&amp;#xa;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;It's as simple as that... what would normally take minutes takes seconds (once the database setup is done).  If you haven't used eXist yet I highly recommend it.&lt;br /&gt;&lt;/div&gt;    &lt;br /&gt;&lt;div&gt;This article is &lt;a href="http://andrewjwelch.com/code/xslt/eXist/Combine-with-eXist.html"&gt;repeated here&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-4554652156325501454?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/4554652156325501454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=4554652156325501454&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/4554652156325501454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/4554652156325501454'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/07/combining-xslt-20s-grouping-with-exist.html' title='Combining XSLT 2.0&apos;s Grouping with eXist'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-23815417.post-2637733168077463174</id><published>2007-07-11T12:42:00.000Z</published><updated>2007-07-11T13:02:54.950Z</updated><title type='text'>CSV to XML transform updated</title><content type='html'>I've posted a new version of the &lt;a href="http://andrewjwelch.com/code/xslt/csv/csv-to-xml_v2.html"&gt;CSV to XML transform&lt;/a&gt;. This version handles nested quotes correctly - the previous version would generate extra tokens either side of the quoted value.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/23815417-2637733168077463174?l=ajwelch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://andrewjwelch.com/code/xslt/csv/csv-to-xml_v2.html' title='CSV to XML transform updated'/><link rel='replies' type='application/atom+xml' href='http://ajwelch.blogspot.com/feeds/2637733168077463174/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=23815417&amp;postID=2637733168077463174&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2637733168077463174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/23815417/posts/default/2637733168077463174'/><link rel='alternate' type='text/html' href='http://ajwelch.blogspot.com/2007/07/csv-to-xml-transform-updated.html' title='CSV to XML transform updated'/><author><name>Andrew Welch</name><uri>http://www.blogger.com/profile/18036830798733728742</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13912057590185615949'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry></feed>