2009-09-18

Grouping XML nodes using XSL 2.0

Man, that was painful!!! Curse those XML Committee morons that should have designed a transformation language based on Perl, rather than come with the atrocity that is XSL!

So, let's say you have the following XML (This is a list of cross references between 8051 banks, custom generated by a popular disassembler), which you already sorted using the other XSL I posted today:
<banks>
<B0_000E/>
<B0_000E/>
<B0_000E/>
<B0_000E/>
<B0_49F8/>
<B0_49F8/>
<B0_49F8/>
<B0_49F8/>
<B0_49F8/>
<B0_49F8/>
<B0_520A/>
<B0_520A>
<BD_2E9D/>
<BD_C4B5/>
</B0_520A>
<B0_520A/>
<B0_54F9/>
<B0_54F9/>
<B0_54F9>
<BB_E851/>
</B0_54F9>
<B0_54F9/>
<B0_59EC>
<B0_012F/>
</B0_59EC>
<B0_59EC>
<BE_012F/>
</B0_59EC>
<B0_59EC>
<BF_012F/>
</B0_59EC>
<B0_CC5E/>
<B0_CC5E>
<BC_DAA6/>
<BC_DF2E/>
</B0_CC5E>
<B0_CC5E>
<BB_DA1C/>
</B0_CC5E>
<B0_CC5E/>
<B0_E022/>
<B0_E022>
<BE_E439/>
<BE_E458/>
<BE_EE05/>
<BE_EE28/>
<BE_F15B/>
<BE_F17A/>
<BE_F8E5/>
<BE_F8F9/>
</B0_E022>
<B0_E022>
<BF_3BF0/>
<BF_6C6B/>
<BF_7F7E/>
<BF_7F9D/>
<BF_7FE0/>
<BF_7FFF/>
<BF_CD2C/>
<BF_D103/>
<BF_D126/>
<BF_D146/>
<BF_D169/>
</B0_E022>
<B0_E022/>
<B0_E022>
<BC_CE5A/>
</B0_E022>
<B0_E022/>
<B0_E022/>
<B0_E201/>
<B0_E201/>
</banks>
Obviously now, you would like to move all the sub nodes into a single one and remove all the duplicates to obtain a file like this:
<banks>
<B0_000E/>
<B0_49F8/>
<B0_520A>
<BD_2E9D/>
<BD_C4B5/>
</B0_520A>
<B0_54F9>
<BB_E851/>
</B0_54F9>
<B0_59EC>
<B0_012F/>
<BE_012F/>
<BF_012F/>
</B0_59EC>
<B0_CC5E>
<BC_DAA6/>
<BC_DF2E/>
<BB_DA1C/>
</B0_CC5E>
<B0_E022>
<BE_E439/>
<BE_E458/>
<BE_EE05/>
<BE_EE28/>
<BE_F15B/>
<BE_F17A/>
<BE_F8E5/>
<BE_F8F9/>
<BF_3BF0/>
<BF_6C6B/>
<BF_7F7E/>
<BF_7F9D/>
<BF_7FE0/>
<BF_7FFF/>
<BF_CD2C/>
<BF_D103/>
<BF_D126/>
<BF_D146/>
<BF_D169/>
<BC_CE5A/>
</B0_E022>
<B0_E201/>
</banks>

Well, using the XSL (2.0) below, now you can!
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="/">
<banks> <!-- Our XML root element. Modify according to yours! -->
<!-- Select all nodes that are immediate children of our root
element ("/*/*") and group them according to their node
names ("local-name()") -->
<xsl:for-each-group select="/*/*" group-by="local-name()">
<!-- Output a (single) node for each of these groups -->
<xsl:element name="{local-name()}">
<!-- NB: To remove empty nodes, move the <xsl:element>
section inside the <xsl:for-each-group> below, and use
{local-name(parent::*)} instead of {local-name()} -->
<!-- Select all the children nodes from the above group
("current-group()/*) - Don't bother to group them. -->
<xsl:for-each-group select="current-group()/*" group-by=".">
<!-- Here we copy the whole (group) branch as is -->
<xsl:copy-of select="current-group()"/>
</xsl:for-each-group>
</xsl:element>
</xsl:for-each-group>
</banks> <!-- Close our root -->
</xsl:template>
</xsl:stylesheet>

2009-09-17

Now, Vista, where's my file actually?

If security done backwards is better than no security at all, then Microsoft are the kings of running in reverse.

You might have used some programs to edit files in "System" directories, only to wonder why the files that are there don't reflect your changes.
The answer is that, when Microsoft is unhappy about the location you edit a file in (eg: C:\Program Files (x86)\<some program>\<some file>), it creates a duplicate in C:\Users\<your user name>\AppData\Local\VirtualStore\ (eg: C:\Users<your user name>\AppData\Local\VirtualStore\Program Files (x86)\<some program>\<some file>).

Pretty sure that having 2 copies of one file is gonna be a thrill for people who want to backup their data...

...and the class blurb for SyntaxHighlighter

Great tool that SyntaxHighlighter. Followed this post and got exactly what's written on the tin.

Now, since I'm of a forgetful nature, the syntax for the <pre> modifier is <pre class="brush: <brushname>"> where <brushname> is one of text, bash, xml, cpp or any of the keywords highlighted here.

...and the UltraEdit Macro script to convert code for blogger publishing

Courtesy of Rob, to which I added the & -> &amp; conversion:
InsertMode
ColumnModeOff
HexOff
UnixReOff
IfSel
Find SelectText "&"
Replace All "&amp;"
Find SelectText "<"
Replace All "&lt;"
Find SelectText ">"
Replace All "&gt;"
Find SelectText """
Replace All "&quot;"
Find SelectText "^t"
Replace All " "
Else
GotoLine 1 1
Find "&"
Replace All "&amp;"
Find "<"
Replace All "&lt;"
Find ">"
Replace All "&gt;"
Find """
Replace All "&quot;"
Find "^t"
Replace All " "
EndIf

XSLT script to sort nodes alphabetically

The simple XSL sample you have been looking for. Works great with Saxon. "version" set to 2.0 here to prevent a warning with the latest version of Saxon, but you can change it to 1.0 for older XSLT engines.
<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*" />

<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>

<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:sort select="@typeName"/>
<xsl:sort select="name(.)"/>
<xsl:sort />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

Oh, and the saxon commandline syntax to apply an XSL (which should really be provided in a straightforward fashion in the commandline help) is:
C:\>saxon -s:banks.xml -xsl:sort.xsl -o:banks_sorted.xml

2009-09-16

.NET, or the solution to a problem that didn't exist

So, you're using Visual Studio Express and you want to use that supposedly nice SerialPort .NET class (after spending more than half a day trying to make a standard COM port app that can talk nicely to putty - hint: don't rely on anything else but putty's actual port initialization code if you want something that works!).

And yeah, you're like me, so you don't care much about whatever the hell .NET brings to the OO paradigm. You just want to use a class that works ASAP, so you followed the tutorials on the net that tell you: "Just add a 'using namespace System::IO::Ports;' and then you can define a SerialPort instance", which you promptly did.

But now, of course, you're getting the bleeping error:
error C2039: 'Ports' : is not a member of 'System::IO'
Yeah, that's because with all this supposedly improved technology, Visual Studio is still not able to tell you that you need to add a
#include "system.dll"

before you can see ALL of the System namespace. "Intellisense", my ass!

In short: C0239 = need to add a #include to a dll (?!?, when did that become a C++ standard) BEFORE you can start using "using namespace"

Oh, and good luck figuring that one out if you don't (want to) have a clue about .NET...