title: Calculating distance of context node from nearest preceding text node
body: <p>I wish to record how many elements intervene between a context node and the nearest preceding text node. What I do now is the following:</p>
  
  <pre><code>xquery version "3.0";
  
  let $text :=
  &lt;text&gt;
      &lt;p n="1"&gt;text-node&lt;pb/&gt;&lt;lb/&gt;&lt;seg&gt;text-node&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
      &lt;p n="2"&gt;text-node&lt;pb/&gt;&lt;lb/&gt;&lt;seg&gt;&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
      &lt;p n="3"&gt;text-node&lt;pb/&gt;&lt;seg&gt;&lt;lb/&gt;&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
      &lt;p n="4"&gt;text-node&lt;pb/&gt;&lt;seg&gt;text-node&lt;lb/&gt;&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
      &lt;p n="5"&gt;text-node&lt;pb/&gt;text-node&lt;seg&gt;&lt;lb/&gt;&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
      &lt;p n="6"&gt;&lt;seg&gt;text-node&lt;pb/&gt;&lt;lb/&gt;&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
      &lt;p n="7"&gt;text-node&lt;seg&gt;&lt;pb/&gt;&lt;lb/&gt;&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
      &lt;p n="8"&gt;text-node&lt;seg&gt;&lt;pb/&gt;&lt;cb/&gt;&lt;lb/&gt;&lt;context-node&gt;context-node&lt;/context-node&gt;text-node&lt;/seg&gt;&lt;/p&gt;
  &lt;/text&gt;
  let $predicate := 'context-node'
  return
      for $element at $i in $text/element()
      let $context-node := $element//element()[. eq $predicate]
      return
          &lt;context-text-distance n="{$i}"&gt;{
          if ($context-node/preceding-sibling::node()[1] instance of text() or $context-node/parent::element()/child::node()[1] is $context-node)
          then 1
          else 
              if ($context-node/preceding-sibling::node()[2] instance of text() or $context-node/parent::element()/child::node()[2] is $context-node)
              then 2
              else
                  if ($context-node/preceding-sibling::node()[3] instance of text() or $context-node/parent::element()/child::node()[3] is $context-node)
                  then 3
                  else ()
          }&lt;/context-text-distance&gt;
  </code></pre>
  
  <p>This returns the right answers and of course I could go on like this - a number higher than 5 is extremely unlikely - but I am curious to know whether it is possible to calculate this distance without testing each individual possibility?</p>
  
  <p>I take point of departure in the context node, an element node. I want to see how many preceding element nodes have to be traversed in order to reach a text node sibling of the context node or if the context node is the first child node of its parent. If one of these requirements is satisfied, the distance is 1. Thus in the example 1, the preceding-sibling of the context node is a text node, and the distance is therefore 1. In the example 2, a seg element is parent of the context node, and since the context node is its first child, the distance is again 1. In the example 3, the encompassing seg element comes before the empty lb element, so the count is 2. In the example 4, a text node comes before the empty lb element, so the count is again 2. Example 5 is actually a replay of example 3 and could be deleted. In example 6, two empty elements "intervene" between the context node and the nearest preceding text node, so the distance is 3. In example 7, the parent seg node has the same effect on the distance count as the text node of example 6. Example 8 returns empty, since the distance is 4 which is not covered.</p>
  
  <p>I use this in order to determine in which order to extract and insert standoff markup in text. Empty elements have the same offset, but have to be inserted in a determinate order. All offsets are calculated in relation to text nodes. A context node which is the first child of its parent has the offset of 0, as if there was a text node.</p>