xsl-list
[Top] [All Lists]

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

2008-04-27 06:23:23
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"/>
      <node name="bat" id="b4"/>
    </node>
    <node>
      <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[(_at_)name] except 
$bn/ancestor::node)/'../'"/>
  <xsl:value-of separator="/" 
select="($bn/ancestor-or-self::node[(_at_)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. 
________________________________________________________________________

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--