Domanda

I have a query which attaches unique references to elements in @xml:ids. It works as it should, but how can I avoid the kludgy repetition of the function local:add-references()?

xquery version "3.0";

declare namespace tei="http://www.tei-c.org/ns/1.0";

declare function local:change-attributes($node as node(), $new-name as xs:string, $new-content as item(), $action as xs:string, $target-element-names as xs:string+, $target-attribute-names as xs:string+) as node()+ {
    if ($node instance of element()) 
    then
        element {node-name($node)} 
        {
            if ($action = 'attach-attribute-to-element' and name($node) = $target-element-names)
            then ($node/@*, attribute {$new-name} {$new-content})
            else 
            $node/@*
            ,
            for $child in $node/node()
            return $child        
        }
    else $node
};

declare function local:add-references($element as element()) as element() {
    element {node-name($element)}
    {$element/@*,
    for $child in $element/node()
        return
            if ($child instance of element() and $child/../@xml:id)
            then
                if (not($child/@xml:id))
                then
                    let $local-name := local-name($child)
                    let $preceding-siblings := $child/preceding-sibling::element()
                    let $preceding-siblings := count($preceding-siblings[local-name(.) eq $local-name])
                    let $following-siblings := $child/following-sibling::element()
                    let $following-siblings := count($following-siblings[local-name(.) eq $local-name])
                    let $seq-no := 
                        if ($preceding-siblings = 0 and $following-siblings = 0)
                        then ''
                        else $preceding-siblings + 1
                    let $id-value := concat($child/../@xml:id, '-', $local-name, if ($seq-no) then '-' else '', $seq-no)
                    return
                        local:change-attributes($child, 'xml:id', $id-value, 'attach-attribute-to-element', ('body', 'quote', 'titlePage','text','p','div','front','head','titlePart'), '')
                else local:add-references($child)
            else
                 if ($child instance of element())
                 then local:add-references($child)
                 else $child
      }
};

let $doc := 
<TEI xmlns="http://www.tei-c.org/ns/1.0">
   <text xml:id="hamlet">
      <front>
         <div>
            <p>…</p>
         </div>
         <titlePage>
            <titlePart>…</titlePart>
         </titlePage>
         <div>
            <p>…</p>
         </div>
      </front>
      <body>
         <p>…</p>
         <quote>…</quote>
         <p>…</p>
         <div>
            <head>…</head>
            <p>…</p>
            <quote>…</quote>
            <p>…</p>
            <p>…</p>
         </div>
         <div>
            <lg>
               <l>…</l>
               <l>…</l>
            </lg>
         </div>
      </body>
   </text>
</TEI>

return local:add-references(local:add-references(local:add-references(local:add-references(local:add-references($doc)))))

There must be some way to call a function recursively, wrapping it up in itself a certain number of times, using the depth of the document.

The problem is that a certain depth of elements has to wait until their parent elements have been given @xml:ids before they can get their own (the top level has to be seeded with an @xml:id).

È stato utile?

Soluzione

This is probably not the best solution, but you might just test if applying the function still changes anything, and cancel otherwise:

declare function local:add-references-recursively($now as element()) as element() {
  let $next := local:add-references($now)
  return
    if (deep-equal($now, $next))
    then $now
    else local:add-references-recursively($next)
};

And call using

local:add-references-recursively($doc)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top