我曾经为用于为 XSLT 2.0 和相关规范生成勘误表的样式表编写了与此类似的目的的代码。你可能会发现它给了你一些想法。这包含检查规范中没有文本受到多个勘误“影响”的逻辑。代码如下(它使用 saxon:evaluate 因为定义勘误表的 XML 文档包含指示基本文档中的哪些部分“受影响”的 XPath 表达式)。
这里的一个关键目标不是确定两个 XPath 表达式是否选择重叠节点,而是确定 N 个这样的表达式是否有任何重叠,其中表达式是事先不知道的——我们希望这样做而不寻找所有表达式对之间的重叠。
这里有一个有趣的表达式:count($x) != count($x/.)。这里 ($x/.) 用于强制从节点序列中消除重复节点,因此测试是询问重复消除是否会删除任何节点,即 $x 是否包含任何重复。
<!-- The following template checks that there is no element in the source document
that is replaced or deleted by more than one erratum -->
<xsl:template name="check-for-conflicts">
<xsl:variable name="directly-affected-elements"
select="er:eval-all(/er:errata/er:erratum[not(@superseded)]//er:old-text[not(starts-with(@action, 'insert-'))])"/>
<xsl:variable name="all-affected-elements"
select="for $e in $directly-affected-elements return $e/descendant-or-self::*"/>
<xsl:if test="count($all-affected-elements) != count($all-affected-elements/.)">
<!-- we now know there are duplicates, we just need to identify them... -->
<xsl:for-each-group select="$all-affected-elements" group-by="generate-id()">
<xsl:if test="count(current-group()) gt 1">
<xsl:variable name="id" select="(ancestor::*/@id)[last()]"/>
<xsl:variable name="section" select="$spec/key('id',$id)"/>
<xsl:variable name="section-number">
<xsl:number select="$section" level="multiple" count="div1|div2|div3|div4"/>
</xsl:variable>
<xsl:variable name="loc" select="er:location($section, .)"/>
<p style="color:red">
<xsl:text>WARNING: In </xsl:text>
<xsl:value-of select="$section-number, $section/head"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="$loc"/>
<xsl:text>) Element is affected by more than one change</xsl:text>
</p>
</xsl:if>
</xsl:for-each-group>
</xsl:if>
</xsl:template>
<!-- Support function for the check-for-conflicts template.
This function builds a list (retaining duplicates) of all elements
in the source document directly affected by a replacement or deletion.
"Directly affected" means that the element is explicitly selected for replacement
or deletion; the descendants of this element are indirectly affected. -->
<xsl:function name="er:eval-all" as="element()*">
<xsl:param name="in" as="element(er:old-text)*"/>
<xsl:for-each select="$in">
<xsl:variable name="id" select="@ref"/>
<xsl:variable name="section" select="$spec/key('id',$id)"/>
<xsl:variable name="exp" select="@select"/>
<xsl:variable name="nodes" select="$section/saxon:evaluate($exp)"/>
<xsl:sequence select="$nodes"/>
</xsl:for-each>
</xsl:function>