tags:

views:

72

answers:

2

So the title of this post may be a little misleading, but it's the best I can come up with. I'm working on a project that uses TEI for encoding texts. One of the requirements of my current work is to write XSL transformations to render the XML-encoded texts as HTML. For the most part, no problem. I'm kind of stuck on this issue, though:

    <l>There is <delSpan spanTo="A1"/>deleted text spanning</l>
    <l>multiple lines here.<anchor xml:id="A1"/> More text...</l>

Or, in other instances:

    <delSpan spanTo="A2"/>
    <l>Several deleted lines -- the delspan marker can appear </l>
    <l>outside of an l element.... </l>
    <anchor xml:id="A2"/>

(In case you're not familiar with TEI: l = a line of text; delSpan = span of deleted text that includes more than 1 line, page, or smaller unit.)

The goal is to display the text between the delSpan (A1) and its corresponding anchor (A1) -- "deleted text spanning / multiple lines here" -- with some formatting that indicates the deletion (e.g., text-decoration="line-through"). Right now, there's a template for "l" elements that handles most text formatting--or at least calls other templates to do it.

But these singleton tags are an anomaly; all other formatting/markup is accomplished with tags that actually contain the text they're meant to format. Am I right in assuming that I need to process the delSpan and anchor elements in the "l" template? What's the most elegant way to approach this problem and handle the pseudo-overlapping elements?

Sorry if this is a noob question or if I haven't given enough information. I'm primarily a C/C++ programmer with little XSLT experience, so I appreciate any suggestions.

+1  A: 

Your main issue here is that the delSpan elements are not parents and that its ending and closing "element" are an empty element (anchor for closing). The link between opening and closing is done by referencing an xml:id tag.

There're multiple solutions to this issue, here's one from the top of my head, assuming you use XSLT 1.0 (it's much easier with XSLT 2.0). The idea is simple: find all siblings and apply them, but only if they are themselves followed by a sibling anchor with the correct xml:id:

<xsl:template match="delSpan">
   <xsl:variable select="@spanTo" name="spanTo" />
   <xsl:apply-templates select="following-sibling::*[following-sibling::anchor[@xml:id = $spanTo]" mode="deleted" />
</xsl:template>

<!--
     do this for all elements you need to treat inside delSpan
     if they have children, remember to use apply-templates with mode deleted
 -->
<xsl:template match="l" mode="deleted">
   <strike><xsl:value-of select="." /></strike>
</xsl:template>

I'm not 100% sure this is entirely correct. It may go wrong if <delSpan> can be nested. If you're lucky, Dimitri Novatchev walks by and has a look, too.

Abel
+2  A: 

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="delSpan|anchor"/>
    <xsl:template match="text()[preceding::delSpan[1]/@spanTo=following::anchor[1]/@xml:id]">
        <span style="text-decoration:line-through;">
            <xsl:value-of select="."/>
        </span>
    </xsl:template>
</xsl:stylesheet>

Output:

<doc>
    <l>There is <span style="text-decoration:line-through;">deleted text spanning</span></l>
    <l><span style="text-decoration:line-through;">multiple lines here.</span> More text...</l>
</doc>

Note: The use of preceding and following axis and their meaning in document order. We don't overrides any previus template matching l elements. delSpan and anchor could also not been striped.

Alejandro
+1 from me for the good answer!
Dimitre Novatchev
Thanks! This is simple and elegant, and makes perfect sense to me now. I'm enjoying the crash course in XSLT.
Andrew Forrester
@Andrew Forrester: You are wellcome! Ask any time.
Alejandro