XML/XSLT stylesheet/function
Define and use function
<source lang="xml">
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>
</source>
local function
<source lang="xml">
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/>
</source>