Subject: Re: XSLT 2.0 compability issue occured in topicmerge.XSL in DITA 1.5.1
From: team wise <dfanster@xxxxxxxxx>
Date: Tue, 19 Jun 2012 17:25:59 +0800
|
Hi Michael,
Many thanks for your prompt response. You are right that XSLT 1.0
surpresses the error message or it is recoverable.
I have a favor to ask what kind of a running sample would be desirable
for you to spot the error.
I have tried replacing the line <xsl:variable
name="topicrefClass"><xsl:value-of select="@class"/></xsl:variable>
with this line <xsl:variable name="topicrefClass"
select="string(@class)"/> , but made no difference.
I grealty appeciate your time again.
2012/6/19, Michael Kay <mike@xxxxxxxxxxxx>:
>
>
> On 19/06/2012 07:25, team wise wrote:
>> Hi there,
>> When dealing with a XSL style sheet transition to XSLT 2.0, I just
>> encountered with this following error message:
>>
>> [xslt]
>> D:\InfoShare\AppSOMC\Utilities\DITA-OT\SEMC_Infoshare\xsl\topicmerge.xsl:112:
>> Fatal Error! An attribute node (id) cannot be created after the
>> children of the containing element ( I just commented out line 112 and
>> subsequent lines).
>> [xslt]
>> D:\InfoShare\AppSOMC\Utilities\DITA-OT\SEMC_Infoshare\xsl\topicmerge.xsl:120:
>> Fatal Error! An attribute node (id) cannot be created after the
>> children of the containing element
> This is a "recoverable error" in XSLT 1.0: processors are allowed to
> recover by ignoring the attribute. But they aren't required to recover,
> they can also treat it as fatal. In XSLT 2.0 the WG decided to improve
> interoperability by making it a hard error, so all processors must
> report it.
>
> Despite trying to reconstitute your stylesheet with the original line
> numbers, I haven't been able to spot where the code is wrong. There
> should be a Saxon stack trace giving more detail of the call stack at
> the point of failure; alternatively running with -T will give you output
> that is voluminous but very informative. I'm happy to help debug it if
> you can provide a runnable sample.
>
> In general the fix is to make sure you always output the attributes of
> an element before outputting its child nodes.
>
> I noticed, incidentally, that the stylesheet is making liberal use of
> this kind of construct:
>
> <xsl:variable name="topicrefClass"><xsl:value-of
> select="@class"/></xsl:variable>
>
> when it should be doing
>
> <xsl:variable name="topicrefClass" select="string(@class)"/>
>
> Saxon does its best to optimize this, but it can only do so when it's
> possible to analyze all uses of the variable to ensure that the variable
> is always used as a string. When this isn't possible, you incur the high
> cost of constructing a temporary tree to hold the value, which is a very
> heavyweight data structure compared with a single string. I've seen
> stylesheets speeded up by a factor of 5 by fixing this problem.
>
> Michael Kay
> Saxonica
>>
>> There are a couple of threads over the Internet that are related to
>> the error , for example, Michael commented on a similar problem,
>> however, I am bitter at my wit ends to work out a solution that works
>> in this context.
>>
>> We implement Saxon 9. stylesheet is written in XSLT 1.0 as follows:
>>
>> <?xml version="1.0" encoding="UTF-8" ?>
>> <!-- This file is part of the DITA Open Toolkit project hosted on
>> Sourceforge.net. See the accompanying license.txt file for
>> applicable licenses.-->
>>
>> <!-- book.xsl
>> | Merge DITA topics with "validation" of topic property
>> *-->
>> <xsl:stylesheet version="1.0"
>> xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
>>
>> <!-- Include error message template -->
>> <xsl:import href="common/output-message.xsl"/>
>> <!-- Set the prefix for error message numbers -->
>> <xsl:variable name="msgprefix">DOTX</xsl:variable>
>>
>> <xsl:variable name="xml-path"></xsl:variable>
>>
>> <xsl:output method="xml" encoding="utf-8" />
>>
>> <xsl:template match="/*">
>> <xsl:element name="{name()}">
>> <xsl:apply-templates select="@*" mode="copy-element"/>
>> <xsl:apply-templates select="*"/>
>> </xsl:element>
>> </xsl:template>
>>
>> <xsl:template match="/*/*[contains(@class,' map/topicmeta ')]"
>> priority="1">
>> <xsl:apply-templates select="." mode="copy-element"/>
>> </xsl:template>
>>
>> <xsl:template match="/*[contains(@class,' map/map
>> ')]/*[contains(@class,' topic/title ')]">
>> <xsl:apply-templates select="." mode="copy-element"/>
>> </xsl:template>
>>
>> <xsl:template match="*[contains(@class,' map/topicmeta ')]"/>
>> <xsl:template match="*[contains(@class,' map/navref ')]"/>
>> <xsl:template match="*[contains(@class,' map/reltable ')]"/>
>> <xsl:template match="*[contains(@class,' map/anchor ')]"/>
>>
>> <xsl:template match="*[contains(@class,' map/topicref
>> ')][@href][not(@href='')][not(@print='no')]">
>> <xsl:variable name="topicrefClass"><xsl:value-of
>> select="@class"/></xsl:variable>
>> <xsl:comment>Start of imbed for<xsl:value-of
>> select="@href"/></xsl:comment>
>> <xsl:choose>
>> <xsl:when test="@format and not(@format='dita')">
>> <!-- Topicref to non-dita files will be ingored in PDF
>> transformation -->
>> <xsl:call-template name="output-message">
>> <xsl:with-param name="msgnum">049</xsl:with-param>
>> <xsl:with-param name="msgsev">I</xsl:with-param>
>> </xsl:call-template>
>> </xsl:when>
>> <xsl:when test="contains(@href,'#')">
>> <xsl:variable name="sourcefile"><xsl:value-of
>> select="substring-before(@href,'#')"/></xsl:variable>
>> <xsl:variable name="sourcetopic"><xsl:value-of
>> select="substring-after(@href,'#')"/></xsl:variable>
>> <xsl:variable name="targetName"><xsl:value-of
>> select="name(document($sourcefile,/)//*[@id=$sourcetopic][contains(@class,'
>> topic/topic ')][1])"/></xsl:variable>
>> <xsl:if test="$targetName and not($targetName='')">
>> <xsl:element name="{$targetName}">
>> <xsl:apply-templates
>> select="document($sourcefile,/)//*[@id=$sourcetopic][contains(@class,'
>> topic/topic ')][1]/@*" mode="copy-element"/>
>> <xsl:attribute name="refclass"><xsl:value-of
>> select="$topicrefClass"/></xsl:attribute>
>> <xsl:apply-templates
>> select="document($sourcefile,/)//*[@id=$sourcetopic][contains(@class,'
>> topic/topic ')][1]/*" mode="copy-element">
>> <xsl:with-param name="src-file"><xsl:value-of
>> select="$sourcefile"/></xsl:with-param>
>> </xsl:apply-templates>
>> <xsl:apply-templates/>
>> </xsl:element>
>> </xsl:if>
>> </xsl:when>
>> <!-- If the target is a topic, as opposed to a ditabase mixed file
>> -->
>> <xsl:when test="document(@href,/)/*[contains(@class,' topic/topic
>> ')]">
>> <xsl:variable name="targetName"><xsl:value-of
>> select="name(document(@href,/)/*)"/></xsl:variable>
>> <xsl:if test="$targetName and not($targetName='')">
>> <xsl:element name="{$targetName}">
>> <xsl:apply-templates select="document(@href,/)/*/@*"
>> mode="copy-element"/>
>> <xsl:attribute name="refclass"><xsl:value-of
>> select="$topicrefClass"/></xsl:attribute>
>> <!-- If the root element of the topic does not contain an id
>> attribute, then generate one.
>> Later, we will use these id attributes as anchors for PDF
>> bookmarks. -->
>> <xsl:if test="not(document(@href,/)/*/@id)">
>> <xsl:attribute name="id"><xsl:value-of
>> select="generate-id()"/></xsl:attribute>
>> </xsl:if>
>> <xsl:apply-templates select="document(@href,/)/*/*"
>> mode="copy-element">
>> <xsl:with-param name="src-file"><xsl:value-of
>> select="@href"/></xsl:with-param>
>> </xsl:apply-templates>
>> <xsl:apply-templates/>
>> </xsl:element>
>> </xsl:if>
>> </xsl:when>
>> <!-- Otherwise: pointing to ditabase container; output each topic
>> in the ditabase file.
>> The refclass value is copied to each of the main topics.
>> If this topicref has children, they will be treated as
>> children of the<dita> wrapper.
>> This is the same as saving them as peers of the topics in the
>> ditabase file. -->
>> <xsl:otherwise>
>> <xsl:for-each select="document(@href,/)/*/*">
>> <xsl:element name="{name()}">
>> <xsl:apply-templates select="@*" mode="copy-element"/>
>> <xsl:attribute name="refclass"><xsl:value-of
>> select="$topicrefClass"/></xsl:attribute>
>> <xsl:apply-templates select="*" mode="copy-element"/>
>> </xsl:element>
>> </xsl:for-each>
>> <xsl:apply-templates/>
>> </xsl:otherwise>
>> </xsl:choose>
>> </xsl:template>
>>
>> <xsl:template match="*[contains(@class,' map/topicref ')][not(@href)]">
>> <xsl:element name="{name()}">
>> <xsl:apply-templates select="@*" mode="copy-element"/>
>> <xsl:apply-templates/>
>> </xsl:element>
>> </xsl:template>
>>
>> <!--xsl:template
>> match="*|@*|comment()|processing-instruction()|text()"
>> mode="copy-element">
>> <xsl:param name="src-file"></xsl:param>
>> <xsl:copy>
>> <xsl:apply-templates
>> select="*|@*|comment()|processing-instruction()|text()"
>> mode="copy-element">
>> <xsl:with-param name="src-file"><xsl:value-of
>> select="$src-file"/></xsl:with-param>
>> </xsl:apply-templates>
>> </xsl:copy>
>> </xsl:template-->
>>
>> <xsl:template match="@id" mode="copy-element">
>> <xsl:attribute name="id"><xsl:value-of
>> select="generate-id(.)"/></xsl:attribute>
>> </xsl:template>
>>
>> <xsl:template match="@href" mode="copy-element" priority="1">
>> <xsl:param name="src-file"></xsl:param>
>>
>> <xsl:variable name="file-path">
>> <xsl:call-template name="get-file-path">
>> <xsl:with-param name="src-file">
>> <xsl:value-of select="$src-file"/>
>> </xsl:with-param>
>> </xsl:call-template>
>> <xsl:value-of select="."/>
>> </xsl:variable>
>>
>> <xsl:variable name="file-path-new">
>> <xsl:call-template name="normalize-path">
>> <xsl:with-param name="file-path">
>> <xsl:value-of select="translate($file-path,'\','/')"/>
>> </xsl:with-param>
>> </xsl:call-template>
>> </xsl:variable>
>>
>> <xsl:choose>
>> <xsl:when test="contains(.,'://') or ../@scope='external' or
>> ../@scope='peer'">
>> <xsl:copy/>
>> </xsl:when>
>> <xsl:when test="(parent::*[contains(@class,' topic/xref ')] or
>> parent::*[contains(@class,' topic/link ')]) and (not(../@format) or
>> ../@format='dita' or ../@format='DITA')">
>> <xsl:choose>
>> <xsl:when test="starts-with(.,'#')">
>> <xsl:variable name="refer-path"
>> select="substring-after(.,'#')"/>
>> <xsl:choose>
>> <xsl:when test="contains($refer-path,'/')">
>> <xsl:variable name="topic-id"
>> select="substring-before($refer-path,'/')"/>
>> <xsl:variable name="target-id"
>> select="substring-after($refer-path,'/')"/>
>> <xsl:variable name="href-value">
>> <xsl:value-of
>> select="generate-id(//*[contains(@class,' topic/topic
>> ')][@id=$topic-id]//*[@id=$target-id]/@id)"/>
>> </xsl:variable>
>> <xsl:if test="not($href-value='')">
>> <xsl:attribute
>> name="href"><xsl:text>#</xsl:text><xsl:value-of
>> select="$href-value"/></xsl:attribute>
>> </xsl:if>
>> </xsl:when>
>> <xsl:otherwise>
>> <xsl:variable name="href-value">
>> <xsl:value-of
>> select="generate-id(//*[contains(@class,' topic/topic
>> ')][@id=$refer-path]/@id)"/>
>> </xsl:variable>
>> <xsl:if test="not($href-value='')">
>> <xsl:attribute
>> name="href"><xsl:text>#</xsl:text><xsl:value-of
>> select="$href-value"/></xsl:attribute>
>> </xsl:if>
>> </xsl:otherwise>
>> </xsl:choose>
>> </xsl:when>
>> <xsl:when test="contains(.,'#')">
>> <xsl:variable name="file-name"
>> select="substring-before(.,'#')"/>
>> <xsl:variable name="refer-path"
>> select="substring-after(.,'#')"/>
>> <xsl:variable name="file-name-doc"
>> select="document($file-name,/)"/>
>> <xsl:if test="$file-name-doc and not($file-name-doc='')">
>> <xsl:choose>
>> <xsl:when test="contains($refer-path,'/')">
>> <xsl:variable name="topic-id"
>> select="substring-before($refer-path,'/')"/>
>> <xsl:variable name="target-id"
>> select="substring-after($refer-path,'/')"/>
>> <xsl:variable name="href-value">
>> <xsl:value-of
>> select="generate-id($file-name-doc//*[contains(@class,' topic/topic
>> ')][@id=$topic-id]//*[@id=$target-id]/@id)"/>
>> </xsl:variable>
>> <xsl:if test="not($href-value='')">
>> <xsl:attribute
>> name="href"><xsl:text>#</xsl:text><xsl:value-of
>> select="$href-value"/></xsl:attribute>
>> </xsl:if>
>> </xsl:when>
>> <xsl:otherwise>
>> <xsl:variable name="href-value">
>> <xsl:value-of
>> select="generate-id($file-name-doc//*[contains(@class,' topic/topic
>> ')][@id=$refer-path]/@id)"/>
>> </xsl:variable>
>> <xsl:if test="not($href-value='')">
>> <xsl:attribute
>> name="href"><xsl:text>#</xsl:text><xsl:value-of
>> select="$href-value"/></xsl:attribute>
>> </xsl:if>
>> </xsl:otherwise>
>> </xsl:choose>
>> </xsl:if>
>> </xsl:when>
>> <xsl:otherwise>
>> <xsl:variable name="current-doc" select="document(.,/)"/>
>> <xsl:if test="$current-doc and not($current-doc='')">
>> <xsl:choose>
>> <xsl:when test="$current-doc//*[contains(@class,'
>> topic/topic ')]/@id">
>> <xsl:attribute
>> name="href"><xsl:text>#</xsl:text><xsl:value-of
>> select="generate-id($current-doc//*[contains(@class,' topic/topic
>> ')][1]/@id)"/></xsl:attribute>
>> </xsl:when>
>> <xsl:otherwise><xsl:text>#</xsl:text><xsl:value-of
>> select="generate-id($current-doc//*[contains(@class,' topic/topic
>> ')][1])"/></xsl:otherwise>
>> </xsl:choose>
>> </xsl:if>
>> </xsl:otherwise>
>> </xsl:choose>
>>
>> </xsl:when>
>> <xsl:otherwise>
>> <xsl:attribute name="href">
>> <xsl:value-of select="$file-path-new"/>
>> </xsl:attribute>
>> </xsl:otherwise>
>> </xsl:choose>
>> </xsl:template>
>>
>> <xsl:template name="get-file-path">
>> <xsl:param name="src-file"/>
>> <xsl:if test="contains($src-file,'/')">
>> <xsl:value-of select="substring-before($src-file,'/')"/>
>> <xsl:text>/</xsl:text>
>> <xsl:call-template name="get-file-path">
>> <xsl:with-param name="src-file">
>> <xsl:value-of select="substring-after($src-file,'/')"/>
>> </xsl:with-param>
>> </xsl:call-template>
>> </xsl:if>
>> </xsl:template>
>>
>> <xsl:template name="normalize-path">
>> <xsl:param name="file-path" />
>> <xsl:choose>
>> <xsl:when test="contains($file-path,'..')">
>> <xsl:variable name="firstdir"
>> select="substring-before($file-path, '/')" />
>> <xsl:variable name="newpath"
>> select="substring-after($file-path,'/')" />
>> <xsl:choose>
>> <xsl:when test="$firstdir='..'">
>> <xsl:text>../</xsl:text>
>> <xsl:call-template name="normalize-path">
>> <xsl:with-param name="file-path">
>> <xsl:value-of select="$newpath"/>
>> </xsl:with-param>
>> </xsl:call-template>
>> </xsl:when>
>> <xsl:otherwise>
>> <xsl:variable name="beforedotdot"
>> select="substring-before($file-path,'/..')"></xsl:variable>
>> <xsl:variable name="beforedotdotparent">
>> <xsl:call-template name="parent-path">
>> <xsl:with-param name="pathname"
>> select="$beforedotdot" />
>> </xsl:call-template>
>> </xsl:variable>
>> <xsl:variable name="afterdotdot"
>> select="substring-after($file-path,'../')"></xsl:variable>
>> <xsl:call-template name="normalize-path">
>> <xsl:with-param name="file-path">
>> <xsl:value-of
>> select="concat($beforedotdotparent,$afterdotdot)"/>
>> </xsl:with-param>
>> </xsl:call-template>
>> </xsl:otherwise>
>> </xsl:choose>
>> </xsl:when>
>> <xsl:otherwise>
>> <xsl:value-of select="$file-path"></xsl:value-of>
>> </xsl:otherwise>
>> </xsl:choose>
>> </xsl:template>
>>
>> <xsl:template name="parent-path">
>> <xsl:param name="pathname" />
>> <xsl:choose>
>> <xsl:when test="contains($pathname, '/')">
>> <xsl:value-of select="substring-before($pathname,
>> '/')"/>
>> <xsl:text>/</xsl:text>
>> <xsl:call-template name="parent-path">
>> <xsl:with-param name="pathname"
>> select="substring-after($pathname,'/')"/>
>> </xsl:call-template>
>> </xsl:when>
>> </xsl:choose>
>> </xsl:template>
>>
>> <xsl:template match="processing-instruction()">
>> <xsl:copy></xsl:copy>
>> </xsl:template>
>>
>> </xsl:stylesheet>
>>
>> Are there anyone out there who had similar experience before? How did
>> you manage to overcome it?
>>
>> Thank you in advance
>> Ray
>
>
--
Keep an Exacting Eye for Detail
|