Re: [xsl] XSLT 2.0 compability issue occured in topicmerge.XSL in DITA 1.5.1

Subject: Re: [xsl] 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

Current Thread