[xsl] Comparing direct ancestors

Subject: [xsl] Comparing direct ancestors
From: Spencer Tickner <spencertickner@xxxxxxxxx>
Date: Thu, 24 May 2012 08:40:05 -0700
Hi List,

Thanks in advance for any help.

I've been banging my head around this problem for awhile now and could
use any advice anyone is willing to give. I have the following xml:

<?xml version="1.0"?>
<root>
	<change-begin/>
	<a>
		<p>Foo<change-end/></p>
	</a>
	<change-begin/>
	<b>
		<d>
			<t>Bar</t>
		</d>
		<d>
			<t>Foo</t>
		</d>
	</b>
	<change-end/>
	<p>Nothing <change-begin/>to worry<change-end/> about</p>
</root>

I need to write an xslt (2.0 is fine) that will "move" the
<change-begin/> and <change-end/> elements to be children of the
nearest pre-defined allowable element. So in the example above I have
<p> and <t> as elements that allow the <change-begin/> and
<change-end/> elements. The output should look like this:

<?xml version="1.0"?>
<root>
	<a>
		<p><change-begin/>Foo<change-end/></p>
	</a>
	<b>
		<d>
			<t><change-begin/>Bar</t>
		</d>
		<d>
			<t>Foo<change-end/></t>
		</d>
	</b>
	<p>Nothing <change-begin/>to worry<change-end/> about</p>
</root>

My plan was a multi-pass stylesheet that first took care of the
<change-begin/> then went on to take care of the <change-ends/>. For
brevity I've limit this post to trying to deal with the
<change-begin/>:

<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:variable name="allowed">
	<n>p</n>
	<n>t</n>
</xsl:variable>

<xsl:template match="/">
	<xsl:apply-templates select="root" mode="begins"/>
</xsl:template>

<xsl:template match="*" mode="begins">
<xsl:variable name="name" select="name()"/>
		<!-- ok here we narrow our field of vision -->
        <xsl:choose>
			<!-- Only worry about elements that are possible candidates for
moving changes in -->
            <xsl:when test="$allowed/n[name() = $name]">
                <xsl:choose>
					<!-- Is a sibiling a change-begin tag, or are any ancestors
siblings a change-begin tag? -->
					<xsl:when test="preceding-sibling::change-begin or
(ancestor::*[preceding-sibling::change-begin])">
						<xsl:copy>
							<change-begin/>
		                    <xsl:copy-of select="@*"/>
		                    <xsl:apply-templates mode="begins"/>
						</xsl:copy>
					</xsl:when>
					<xsl:otherwise>
		                <xsl:copy>
		                    <xsl:copy-of select="@*"/>
		                    <xsl:apply-templates mode="begins"/>
		                </xsl:copy>
					</xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy>
                    <xsl:copy-of select="@*"/>
                    <xsl:apply-templates mode="begins"/>
                </xsl:copy>
            </xsl:otherwise>
        </xsl:choose>
</xsl:template>

<!-- remove the begins outside of allowable elements. -->
<xsl:template match="change-begin" mode="begins">
	<xsl:if test="parent::*[name() = $allowed/n/.]">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates mode="begins"/>
        </xsl:copy>
	</xsl:if>
</xsl:template>

</xsl:stylesheet>

So far it doesn't work and is fast becoming a bunch of spaghetti
<choose> statements. If anyone has any ideas I would love to hear
them.

Thanks

Current Thread