[xsl] Re: Re: generating a repeatable unique id

Subject: [xsl] Re: Re: generating a repeatable unique id
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Mon, 30 Jul 2001 10:39:48 -0700 (PDT)
Jeni and Sebastian,

Both of this solutions are not too efficient.

There's a third one:

Using DOM (I'm usng MSXML DOM), one can have the following Javascript code:

var p = style.selectSingleNode("/xsl:stylesheet/xsl:param[@name='selectedNodes']");
p.setAttribute("select", theQuery);
var result = source.transformNode(style.documentElement);

This is a fragment of the source code of the XPath Visualiser, and this code is used
to evaluate successfully any XPath expression entered dynamically through the course
of a session by the user.

The meaning of the above code is that the stylesheet is modified before the
transformation is actually applied -- no need to generate a totally new stylesheet,
nor to "dynamically" evaluate an XPath expression using not too efficient tricks.

So Sebastian, this is one of the most efficient ways (if in any case you'll have to
pass a parameter to the stylesheet) to "dynamically" interprete an XPath expression,
that will be known only at "run time".

Dimitre Novatchev.

Jeni Tennison wrote:

Hi Sebastian,

>> Of course you should use different separators to make it a valid ID
>> so that it can be an HTML anchor, e.g.:
>>   chapter1.section2.div3
> thats fine, I can see that nicely. I still have the problem, though,
> of turning that back into the value for a <xsl:apply-templates>
> select attribute, don't I, when I reenter the document?

Oh, it depends on how you're doing the linking. I was imagining that
you were creating something an index.html document with the links in,
containing things like:

  <p><a href="doc.html#chapter1.section2.div3">...</a></p>

And then had a doc.html document with the anchors for the links,
containing things like:

  <h3 id="chapter1.section2.div3">...</h3>

In other words, that you were creating the links by having the IDs
that you refer to in index.html be the same as the IDs that you use
within doc.html. You can make sure that they're the same by having a
module containing a moded template like:

<xsl:template match="*" mode="id">
  <xsl:for-each select="ancestor-or-self::*">...</xsl:for-each>

Import that module into the stylesheet for index.html and for
doc.html. In the stylesheet generating index.html have:

    <xsl:attribute name="href">
      <xsl:apply-templates select="." mode="id" />

In the stylesheet generating doc.html, have:

    <xsl:attribute name="id">
      <xsl:apply-templates select="." mode="id" />

i.e. the same ID value is used in both because you use the same
template to generate them both.

But perhaps you were meaning that you wanted to only display the part
of the document that you were linking to? Of course in the future all
you'd need to do is generate:

<p><a href="doc.xml#xpointer(/chapter[1]/section[2]/div[3])">...</a></p>

But for now I guess you would generate a link that looked something

<p><a href="doc.xml?xpath=/chapter[1]/section[2]/div[3]">...</a></p>

With some magic on the server end, you'd get an $xpath parameter in
your stylesheet.

You could step through the $xpath parameter to locate and apply
templates to the relevant part of the document, something like:

<xsl:template match="*" mode="xpath">
  <xsl:param name="xpath" />
    <!-- if there is a path -->
    <xsl:when test="$xpath">
      <!-- step is the part before the '/' (if there is one) -->
      <xsl:variable name="step">
          <xsl:when test="contains($xpath, '/')">
            <xsl:value-of select="substring-before($xpath, '/')" />
            <xsl:value-of select="$xpath" />
      <!-- the child's name is the part before the '[' -->
      <xsl:variable name="childName"
                    select="substring-before($step, '[')" />
      <!-- and its index is the part between the []s -->
      <xsl:variable name="childIndex"
                              substring-after($step, '['), ']'))" />
      <!-- so apply templates to that child, passing in the $xpath
           left after the first step -->
      <xsl:apply-templates select="*[name() = $childName]
         <xsl:with-param name="xpath"
                         select="substring-after($xpath, '/')" />
    <!-- if there's no path left, then this is the element we want -->
      <!-- apply templates to it (or do something else) -->
      <xsl:apply-templates select="." />

You could kick it off with something like:

<xsl:template match="/">
  <xsl:apply-templates select="*" mode="xpath">
    <xsl:with-param name="xpath"
                    select="substring-after($xpath, '/')" />

Your other option, the one that you were referring to, would be to
generate an XSLT stylesheet that used the XPath. The generator
stylesheet would contain something like:

<xsl:template match="/">
  <oxsl:template match="/">
    <oxsl:apply-templates select="{$xpath}" />

And you might want to pass the URL for the document in there as well,
to make things easier.



Jeni Tennison

Do You Yahoo!?
Make international calls for as low as $.04/minute with Yahoo! Messenger

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list

Current Thread