Re: [xsl] XSL/XPath 2.0 - most efficient way to find route to target element

Subject: Re: [xsl] XSL/XPath 2.0 - most efficient way to find route to target element
From: Robert Koberg <rob@xxxxxxxxxx>
Date: Sun, 27 Apr 2008 09:22:59 -0400
Here is a slightly more reasonable verion of the XSL. I moved the common
path building to a common template and I am using last() to find the
closest common ancestor in the two sequences (they should be in document
order, right?). But it still doesn't work.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="2.0">

  <xsl:key name="config" match="*" use="@id"/>

  <xsl:template match="/">
    
    <test>
      
      <xsl:apply-templates select="key('config', 'a3')" mode="path">
        <xsl:with-param name="target-id" select="'b3'"/>
      </xsl:apply-templates>
      
      <xsl:apply-templates select="key('config', 'b4')" mode="path">
        <xsl:with-param name="target-id" select="'b3'"/>
      </xsl:apply-templates>
      
      <xsl:apply-templates select="key('config', 'c3')" mode="path">
        <xsl:with-param name="target-id" select="'b3'"/>
      </xsl:apply-templates>
      
    </test>
  </xsl:template>
  
  <xsl:template match="*" mode="path">
    <xsl:param name="target-id"/>
    
    <xsl:variable name="target-anc-and-self">
      <xsl:apply-templates select="key('config', $target-id)"
mode="ancestry"/>
    </xsl:variable>
    
    <xsl:variable name="anc-and-self">
      <xsl:apply-templates select="." mode="ancestry"/>
    </xsl:variable>
    
    <xsl:variable name="common-ancestor" select="($anc-and-self
intersect $target-anc-and-self)[last()]"/>
    <xsl:copy-of select="$common-ancestor"/>
    
    <xsl:variable name="path-to-target">
      <xsl:apply-templates select="." mode="path-up">
        <xsl:with-param name="common-ancestor"
select="$common-ancestor"/>
      </xsl:apply-templates>
      <xsl:apply-templates select="key('config', $target-id)"
mode="path-down">
        <xsl:with-param name="common-ancestor"
select="$common-ancestor"/>
      </xsl:apply-templates>
    </xsl:variable>
    
    <path href="{$path-to-target}"/>
    
  </xsl:template>
  
  <xsl:template match="*" mode="ancestry">
    <xsl:sequence select="ancestor-or-self::*"/>
  </xsl:template>
  
  <xsl:template match="*" mode="path-up">
    <xsl:param name="common-ancestor"/>
    <xsl:choose>
      <xsl:when test=". = $common-ancestor"/>
      <xsl:otherwise>
        <xsl:text>../</xsl:text>
        <xsl:apply-templates select="parent::*" mode="path-up"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template match="*" mode="path-down">
    <xsl:param name="common-ancestor"/>
    <xsl:choose>
      <xsl:when test=". = $common-ancestor"/>
      <xsl:otherwise>
        <xsl:apply-templates select="parent::*" mode="path-down"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="@name"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
</xsl:stylesheet>



On Sun, 2008-04-27 at 09:02 -0400, Robert Koberg wrote:
> Here is what I tried (probably uses too much XSLT 1.0 thinking), but it
> does not work. I tried using intersect to get the nearest common
> ancestor, but I can't seem to get it. What am I doing wrong?
> 
> The XSL is followed by the XML:
> 
> <?xml version="1.0" encoding="UTF-8"?>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> version="2.0">
> 
>   <xsl:key name="config" match="*" use="@id"/>
> 
>   <xsl:template match="/">
>     
>     <test>
>       <xsl:variable name="target"  select="key('config', 'b3')"/>
>       
>       <xsl:variable name="target-anc-and-self">
>         <xsl:apply-templates select="key('config', 'b3')"
> mode="ancestry"/>
>       </xsl:variable>
>       
>       <xsl:variable name="a3-anc-and-self">
>         <xsl:apply-templates select="key('config', 'a3')"
> mode="ancestry"/>
>       </xsl:variable>
>       
>       <xsl:variable name="a3-common-ancestor" select="($a3-anc-and-self
> intersect $target-anc-and-self)[1]"/>
>       <xsl:copy-of select="$a3-common-ancestor"/>
>       
>       <xsl:variable name="a3-to-target">
>         <xsl:apply-templates select="key('config', 'a3')"
> mode="path-up">
>           <xsl:with-param name="common-ancestor"
> select="$a3-common-ancestor"/>
>         </xsl:apply-templates>
>         <xsl:apply-templates select="$target" mode="path-down">
>           <xsl:with-param name="common-ancestor"
> select="$a3-common-ancestor"/>
>         </xsl:apply-templates>
>       </xsl:variable>
>       
>       <path href="{$a3-to-target}"/>
>       
>       <xsl:variable name="b4-anc-and-self">
>         <xsl:apply-templates select="key('config', 'b4')"
> mode="ancestry"/>
>       </xsl:variable>
>       
>       <xsl:variable name="b4-common-ancestor" select="($b4-anc-and-self
> intersect $target-anc-and-self)[1]"/>
>       <xsl:copy-of select="$b4-common-ancestor"/>
>       
>       <xsl:variable name="b4-to-target">
>         <xsl:apply-templates select="key('config', 'b4')"
> mode="path-up">
>           <xsl:with-param name="common-ancestor"
> select="$b4-common-ancestor"/>
>         </xsl:apply-templates>
>         <xsl:apply-templates select="$target" mode="path-down">
>           <xsl:with-param name="common-ancestor"
> select="$b4-common-ancestor"/>
>         </xsl:apply-templates>
>       </xsl:variable>
>       
>       <path href="{$b4-to-target}"/>
>       
>       <xsl:variable name="c3-anc-and-self">
>         <xsl:apply-templates select="key('config', 'c3')"
> mode="ancestry"/>
>       </xsl:variable>
>       
>       <xsl:variable name="c3-common-ancestor" select="($c3-anc-and-self
> intersect $target-anc-and-self)[1]"/>
>       <xsl:copy-of select="$c3-common-ancestor"/>
>       
>       <xsl:variable name="c3-to-target">
>         <xsl:apply-templates select="key('config', 'c3')"
> mode="path-up">
>           <xsl:with-param name="common-ancestor"
> select="$c3-common-ancestor"/>
>         </xsl:apply-templates>
>         <xsl:apply-templates select="$target" mode="path-down">
>           <xsl:with-param name="common-ancestor"
> select="$c3-common-ancestor"/>
>         </xsl:apply-templates>
>       </xsl:variable>
>       
>       <path href="{$c3-to-target}"/>
>       
>     </test>
>   </xsl:template>
>   
>   <xsl:template match="*" mode="ancestry">
>     <xsl:sequence select="ancestor-or-self::*"/>
>   </xsl:template>
>   
>   <xsl:template match="*" mode="path-up">
>     <xsl:param name="common-ancestor"/>
>     <xsl:choose>
>       <xsl:when test=". = $common-ancestor"/>
>       <xsl:otherwise>
>         <xsl:text>../</xsl:text>
>         <xsl:apply-templates select="parent::*" mode="path-up"/>
>       </xsl:otherwise>
>     </xsl:choose>
>   </xsl:template>
>   
>   <xsl:template match="*" mode="path-down">
>     <xsl:param name="common-ancestor"/>
>     <xsl:choose>
>       <xsl:when test=". = $common-ancestor"/>
>       <xsl:otherwise>
>         <xsl:apply-templates select="parent::*" mode="path-down"/>
>         <xsl:text>/</xsl:text>
>         <xsl:value-of select="@name"/>
>       </xsl:otherwise>
>     </xsl:choose>
>   </xsl:template>
>   
> </xsl:stylesheet>
> 
> 
> <?xml version="1.0" encoding="UTF-8"?>
> <node name="foo" id="x">
>   <node name="bar" id="b1">
>     <node>
>       <node/>
>       <node/>
>     </node>
>     <node name="baz" id="b2">
>       <node name="bat" id="b3"/>o;?
>       <node name="bat" id="b4"/>
>     </node>
>     <node>o;?
>       <node name="saz" id="c2">
>         <node name="sat" id="c3"/>
>       </node>
>     </node>
>   </node>
>   <node name="gar" id="a1">
>     <node name="gaz" id="a2">
>       <node name="gat" id="a3"/>
>       <node/>
>     </node>
>     <node>
>       <node/>
>     </node>
>     <node>
>       <node/>
>     </node>
>   </node>
> </node>
> 
> 
> 
> 
> On Sat, 2008-04-26 at 23:43 +0100, David Carlisle wrote:
> > 
> > This doesn't get quite the result you ask for I get ../bat for the last
> > one, also I assume that nodes without a name should not contribute to
> > the path (otherwise I'd need an extra ../ to get out of the unnamed
> > node that contains c2).
> > 
> > <xsl:stylesheet version="2.0"
> > 		xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> > 		xmlns:f="data:,f">
> >  
> > <xsl:key name="id" match="*" use="@id"/>
> > <xsl:variable name="r" select="/"/>
> > <xsl:template match="/">
> > 
> > a3 to b3 <xsl:value-of select="f:path('a3','b3')"/>
> > c3 to b3 <xsl:value-of select="f:path('c3','b3')"/>
> > b4 to b3 <xsl:value-of select="f:path('b4','b3')"/>
> > 
> > </xsl:template>
> > 
> > 
> > <xsl:function name="f:path">
> >  <xsl:param name="a"/>
> >  <xsl:param name="b"/>
> >  <xsl:variable name="an" select="key('id',$a,$r)"/>
> >  <xsl:variable name="bn" select="key('id',$b,$r)"/>
> >  <xsl:value-of>
> >   <xsl:sequence select="($an/ancestor-or-self::node[@name] except $bn/ancestor::node)/'../'"/>
> >   <xsl:value-of separator="/" select="($bn/ancestor-or-self::node[@name] except $an/ancestor::node)/@name"/>
> >  </xsl:value-of>
> > </xsl:function>
> > 
> > </xsl:stylesheet>
> > 
> > ________________________________________________________________________
> > The Numerical Algorithms Group Ltd is a company registered in England
> > and Wales with company number 1249803. The registered office is:
> > Wilkinson House, Jordan Hill Road, Oxford OX2 8DR, United Kingdom.
> > 
> > This e-mail has been scanned for all viruses by Star. The service is
> > powered by MessageLabs. 
> > ________________________________________________________________________

Current Thread