June 2005 Archives

XSLT and DOS Filenames

| No Comments | No TrackBacks

I'm using XSLT to transform a bunch of FrameMaker generated XML files into DITA XML files. One thing that I'm doing in this process is creating a lot of new files. Each level of heading in the FrameMaker file will become a new XML file. The filenames are based either on a processing instruction like this [code lang="xml"][/code] or the contents of the title element.

This gets tricky because both of these places can have any string, not just a valid DOS filename. You see, DOS doesn't like its filenames that include special characters like */+|\=?[];:".<>,'

So, if I have an xml file that includes this fragment:
[code lang="xml"]
What's foo
....

[/code]
Since the title is "What's foo?", the XSLT process will create a file called What's foo?.xml If you try to do anything with that file, DOS will be quite unhappy.

To solve this, I want to remove these special characters, change any spaces to the underscore (_) character, and, just for consistency, make all the letters lowercase. The resulting filename from the above example would then be whats_foo.xml

Luckily, XSLT includes the translate() function. It's designed exactly for this kind of character substitution. The translate function takes three arguments, the string to process (in the above example, "What's foo"), a list of characters that should be translated, and a list of characters that replace the characters in the first list. For example,
[code lang="xml"]translate("foo", "o", "u")[/code]
would return the string "fuu".

Here's how I did I used translate() to fix my filename strings.

First I created two variables, upper and lower. Note that each of these should be on a single line, like they are shown here. Spacing matters! If things wrap in your editor, you will probably get strange results. The upper variable has a space it in right after the capital Z, which encourages a lot of editors (and web browsers) to wrap there. Be careful.
[code lang="xml"]

ABCDEFGHIJKLMNOPQRSTUVWXYZ /+|\=?[];:".<>,'abcdefghijklmnopqrstuvwxyz_

[/code]

I used variables just to make everything easier, since these strings are long. As you saw above, you don't have to use variables.

Notice that lower is shorter than upper. I'm taking advantage of a nice trick in translate(). If a character isn't found in the replacement string, it will be removed. So, if I changed the above example to
[code lang="xml"]translate("foo", "of", "u")[/code]

the result would be "uu", since only the "o" has a matching replacement. You can see that the order of the characters in the second and third strings is very important. If we did this
[code lang="xml"]translate("foo", "fo", "u")[/code]
instead, the result would be "u", since now the "o" doesn't have a matching character in the third string.

To take advantage of this feature in translate(), I've made the first character after the z an underscore. That's in the same location as the space character in the upper variable. The other non-text characters, which are after the space, don't have replacements in lower, so they will be removed by transform()

Pulling all this together, I grab the content I want (in this case the text value of the ./topic/title element), and translate it.

[code lang="xml"]
select="concat
(translate
(normalize-space(./topic/title),
$upper,$lower),'.xml')"/>
[/code]

The translate() function replaces all the characters in the title that match characters in the variable upper (in XSLT, a dollar sign signifies a variable), with the appropriate characters from the lower variable, removing any characters in upper that don't have a mate in lower.

The concat function tacks a .xml on the end of the string.

Using the above example, the translated string will be whats_foo.xml.

Nike Free

| No Comments | No TrackBacks

I bought a pair of Nike Free shoes today. Unlike the ones in the link, mine are blue and yellow.

These shoes are supposed to be more like running barefoot than a typical shoe.

I ran on the treadmill at NikeTown for about five minutes. When I really got into my ChiRunning stance, the shoes felt really good.