Re: [xsl] character entities

Subject: Re: [xsl] character entities
From: Joerg Pietschmann <joerg.pietschmann@xxxxxx>
Date: Wed, 28 Nov 2001 12:37:25 +0100
Dimitre Novatchev <dnovatchev@xxxxxxxxx> wrote:
> > > Also I'd have to go outside XSLT 1.0 ... then there is _nothing_ a
> > > template can do to create that node set, as templates don't create
> > > nodesets.
> I didn't "jump onto this", because generic templates cannot help here -- they
> create/return RTFs, which even when transformed to a nodeset do not contain the
> original nodes but their copies.

I rather thougt of using the equivalent of a function pointer.

For illustration:
1. Foo-elements as previous sibling in document (perhaps included
there as entity)
  <xsl:template ... >
    ...
    <xsl:for-each select="previous-sibling::foo[1]/bar">
      <!-- do stuff -->
      ...
2. Foo-elements perhaps in external document, use copy-of and node-set
  <xsl:template ... >
    ...
    <xsl:variable name="stuff">
      <xsl:choose>
        <xsl:when test="previous-sibling::*[1][self::nag:reference]">
          <xsl:copy-of select="document(previous-sibling::nag:reference[1]/@url)/refitem/foo)"/>
        </xsl:when>
        <xsl:when test="previous-sibling::*[1][self::foo]">
          <xsl:copy-of select="previous-sibling::self::foo"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">Problem getting foo</xsl:message>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:for-each select="xx:node-set($stuff)/bar">
      <!-- do stuff -->
      ...
Obvious disadvantage: the processing stage cannot refer to ancestors
and siblings of foo-elements.
3. Foo-elements perhaps referenced, use indirection for xpaths crossing
the reference boundaries.
  <i:do-stuff/>
  <xsl:template match="i:do-stuff">
    <xsl:param name="reference-node"/>
    <xsl:param name="stuff"/>
    <!-- do stuff -->
  </xsl:template>
  <xsl:template name="get-foo">
    <xsl:param name="f"/>
    <xsl:choose>
      <xsl:when test="previous-sibling::*[1][self::nag:reference]">
        <xsl:apply-template select="$f">
          <xsl:with-param name="reference-node" select="previous-sibling::nag:reference[1]"/>
          <xsl:with-param name="stuff" select="document(previous-sibling::nag:reference[1]/@url)/refitem/foo)"/>
        </xsl:apply-template>
      </xsl:when>
      <xsl:when test="previous-sibling::*[1][self::foo]">
        <xsl:apply-template select="$f">
          <!-- leave reference-node empty -->
          <xsl:with-param name="stuff" select="previous-sibling::self::foo"/>
        </xsl:apply-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message terminate="yes">Problem getting foo</xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  ...
  <xsl:template ... >
    ...
    <xsl:call-template name="get-foo">
      <xsl:with-param name="f" select="document('')/*/i:do-stuff"/>
    </xsl:call-template>
You can use the get-foo template everywhere where you have to gather
foos and pass the actual function to be invoked on the foos as a parameter.
I'm not quite sure whether this is actually a "generic template", until
recently i thought so.
Moreover, in order to reference arbitrary ancestors and siblings from
the conceptual tree, some other templates have to be used
  <xsl:template name="get-previous-sibling-foo">
    <xsl:param name="reference-node"/>
    <xsl:param name="context"/>
    <xsl:param name="f"/>
    <xsl:choose>
      <xsl:when test="$reference-node">
        <xsl:apply-templates select="$f">
          <xsl:with-param name="reference-node" select="$reference-node"/>
          <xsl:with-param name="stuff" select="$reference-node/previous-sibling::foo"/>
          <xsl:with-param name="context" select="$context"/>
        </xsl:apply-templates>
      <xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="$f">
          <xsl:with-param name="reference-node" select="$reference-node"/>
          <xsl:with-param name="stuff" select="$context/previous-sibling::foo"/>
          <xsl:with-param name="context" select="$context"/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
and something similar for ancestors and everything else.
hardcoding all the element names ("foo") into all the templates
above may not be desirable. A xx:evaluate() extension could be used,
or a somewhat crude interpreter of an xml-ified xpath-syntax:
  <xsl:template name="evaluate-simple-xpath">
    <xsl:param name="xpath"/>
    <xsl:param name="reference-node"/>
    <xsl:param name="context"/>
    <xsl:param name="result"/>
    <xsl:param name="f"/>
    <xsl:choose>
      <xsl:when test="not($xpath)">
        <!-- end condition for recursion: xpath empty, invoke function -->
        <xsl:apply-templates select="$f">
          <xsl:with-param name="reference-node" select="$reference-node"/>
          <xsl:with-param name="stuff" select="$result"/>
          <xsl:with-param name="context" select="$context"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:when test="$xpath/step/axis='previous-sibling'">
        <xsl:choose>
          <xsl:when test="generate-id($result)=generate-id($reference-node)">
            <!-- we are crossing the reference boundary -->
            <xsl:call-template name="evaluate-predicate">
              <xsl:param name="$xpath/predicate"/>
              <xsl:with-param name="reference-node" select="$reference-node"/>
              <xsl:with-param name="result" select="$reference-node/previous-sibling::*[name()=$xpath/step/name"/>
              <xsl:with-param name="context" select="$context"/>
              <xsl:with-param name="f" select="$f"/>
            </xsl:call-template>
          <xsl:when>
          <!-- other cases -->
      </xsl:when>
      <!-- other axes -->
    </xsl:choose>
  </xsl:template>
I'm afraid the above won't work, especially as it's probably possible
to encounter other references, but i hope you'll get the general picture.
Invoke this like
  <x:foo-path>
    <step>
      <axis>previous-sibling</axis>
      <name>foo</name>
      <predicate>
        <equals>
          <function>position</function>
          <constant-number>1</constant-number>
        </equals>
      </predicate>
    </step>
  </x:foo-path>
  ...
    <call-template name="eval-simple-xpath">
       <xsl:with-param name="xpath" select="document('')/*/x:foo-path/>
       ...

The above certainly needs a lot of work, it's only the product of
less than thirty minutes of thinking...

Regards
J.Pietschmann

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


Current Thread