XML/XSLT stylesheet/function
Define and use function
File: Data.xml
<?xml version="1.0"?>
<parts>
<part id="p10" idref="p20"/>
<part id="p20" idref="p30 p40"/>
<part id="p30"/>
<part id="p40" idref="p10"/>
</parts>
File: Transform.xml
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:graph="http://wrox.ru/569090/graph"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:function name="graph:refers" as="xs:boolean">
<xsl:param name="links" as="node()" />
<xsl:param name="A" as="node()" />
<xsl:param name="B" as="node()" />
<xsl:variable name="direct" as="node()*">
<xsl:apply-templates select="$links">
<xsl:with-param name="from" select="$A" />
</xsl:apply-templates>
</xsl:variable>
<xsl:sequence
select="if (exists($direct intersect $B)) then true()
else if (some $C in $direct
satisfies graph:refers($links, $C, $B)) then true()
else false()" />
</xsl:function>
</xsl:transform>
local function
File: Data.xml
<?xml version="1.0"?>
<SCENE>
<TITLE>title 1</TITLE>
<STAGEDIR>A</STAGEDIR>
<SPEECH>
<SPEAKER>B</SPEAKER>
<LINE>C</LINE>
<LINE>D</LINE>
<LINE>E</LINE>
<LINE>F</LINE>
</SPEECH>
</SCENE>
File: Transform.xslt
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:local="local-functions.uri" exclude-result-prefixes="xs local"
version="2.0">
<xsl:variable name="speakers" as="xs:string*"
select="for $w in distinct-values(//LINE)
return upper-case($w)" />
<xsl:function name="local:split" as="xs:string*">
<xsl:param name="line" as="xs:string" />
<xsl:sequence select="tokenize($line, "\W")" />
</xsl:function>
<xsl:function name="local:is-character" as="xs:boolean">
<xsl:param name="word" as="xs:string" />
<xsl:sequence select="upper-case($word)=$speakers" />
</xsl:function>
<xsl:template name="scan-line">
<xsl:param name="words" as="xs:string*" />
<xsl:if test="count($words) ge 3">
<xsl:if
test="local:is-character($words[1]) and
lower-case($words[2]) = "and" and
local:is-character($words[3])">
<xsl:value-of select="$words[position() = 1 to 3]"
separator=" " />
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<naming-lines>
<xsl:for-each select="//LINE">
<xsl:call-template name="scan-line">
<xsl:with-param name="words"
select="local:split(.)" />
</xsl:call-template>
</xsl:for-each>
</naming-lines>
</xsl:template>
</xsl:transform>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<naming-lines/>