xsl-list
[Top] [All Lists]

Re: [xsl] XPath 2.0 expression that detects a cycle of references?

2016-03-29 06:19:55

Martin Honnen wrote:

I have no idea how one would have to handle this using XPath 3… any 
suggestions?

Dimitre has an article on using two anonymous function together with "let", 
to implement the recursion:

https://dnovatchev.wordpress.com/2012/10/15/recursion-with-anonymous-inline-functions-in-xpath-3-0-2/

Wow, 2012! And mentioning Roger Costello…

Assuming my original function worked correctly, this would be the XPath 3.0 
version of it:

let $f := function(
  $this as element()+,
  $visited as xs:string*,
  $f1 as function(element(), xs:string*, function(*)) as xs:boolean
  ) as xs:boolean
  {
  let $refId := $this/for-more-info/@idref,
     $refTgt := $this/../item[@id = $refId]
  return
    if (not(exists($refTgt))) then false()
      else if ($refId = $visited) then true()
      else some $e in $refTgt satisfies $f1($e, ($visited, $refId), $f1)
  }
return $f(., (), $f)

At least with Saxon 9.6.0.7 (in Oxygen) in gives the same results.

Thanks a lot for hints, I learned quite a bit!

- Michael

Complete example:

Input:

<?xml version="1.0" encoding="UTF-8"?>
<document>
   <item id="HF">
       <title>Huckleberry Finn</title>
       <for-more-info idref="MT"/>
   </item>
   <item id="MT">
       <name>Mark Twain</name>
       <for-more-info idref="SP"/>
       <for-more-info idref="ZP"/>
   </item>
   <item id="SP">
       <publisher>Springer</publisher>
       <for-more-info idref="HF"/>
   </item>
   <item id="XP">
       <publisher>XPress</publisher>
       <for-more-info idref="HF"/>
   </item>
   <item id="YP">
       <publisher>YPress</publisher>
       <for-more-info idref="ZP"/>
   </item>
   <item id="ZP">
       <publisher>ZPress</publisher>
   </item>
</document>

Stylesheet including both options as attributes cycle2 (XSLT function) and 
cycle3 (Xpath 3.0):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:xs="http://www.w3.org/2001/XMLSchema";
  xmlns:my="my" exclude-result-prefixes="#all"
  version="3.0">

  <xsl:output indent="yes"/>

  <xsl:template match="document">
    <report>
      <xsl:for-each select="item">
        <xsl:copy>
          <xsl:copy-of select="@id"/>
          <xsl:attribute name="cycle2" select="my:CycleFound(., ())"/>
          <xsl:attribute name="cycle3"
            select="
              let $f := function(
                $this as element()+,
                $visited as xs:string*,
                $f1 as function(element(), xs:string*, function(*)) as 
xs:boolean
                ) as xs:boolean
                {
                let $refId := $this/for-more-info/@idref,
                   $refTgt := $this/../item[@id = $refId]
                return
                  if (not(exists($refTgt))) then false()
                    else if ($refId = $visited) then true()
                    else some $e in $refTgt satisfies $f1($e, ($visited, 
$refId), $f1)
                }
              return $f(., (), $f)
                    "
          />
        </xsl:copy>
      </xsl:for-each>
    </report>
  </xsl:template>

  <xsl:function name="my:CycleFound" as="xs:boolean">
    <xsl:param name="this" as="element()?"/>
    <xsl:param name="visited" as="xs:string*"/>
    <xsl:variable name="refId" select="$this/for-more-info/@idref" 
as="xs:string*"/>
    <xsl:variable name="refTgt" select="$this/../item[@id = $refId]" 
as="element()*"/>

    <xsl:sequence select="
    if (not(exists($refTgt))) then false()
    else if ($refId = $visited) then true()
    else some $e in $refTgt satisfies my:CycleFound($e, ($visited, $e/@id))
    "/>
  </xsl:function>

</xsl:stylesheet>

--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

<Prev in Thread] Current Thread [Next in Thread>