Recipe15.3.Automating the Insertion of Debug Output


Recipe 15.3. Automating the Insertion of Debug Output

Problem

You want to transform your s tylesheet into another stylesheet that is instrumented with debug traces.

Solution

Oliver Becker developed a handy stylesheet transformation that takes any input stylesheet and produces an output stylesheet with trace instrumentation:

<!--    Trace utility, modifies a stylesheet to produce trace messages    Version 0.2    GPL (c) Oliver Becker, 2002-02-13    obecker@informatik.hu-berlin.de -->     <xsl:transform version="1.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   xmlns:trace="http://www.obqo.de/XSL/Trace"   xmlns:alias="http://www.w3.org/TransformAlias"   exclude-result-prefixes="alias">      <xsl:namespace-alias stylesheet-prefix="alias" result-prefix="xsl" />       <!-- <xsl:output indent="yes" /> -->       <!-- XSLT root element -->   <xsl:template match="xsl:stylesheet | xsl:transform">     <xsl:copy>       <!-- We need the trace namespace for names and modes -->       <xsl:copy-of select="document('')/*/namespace::trace" />       <!-- ditto: perhaps a namespace was used only as attribute value -->       <xsl:copy-of select="namespace::*|@*" />       <xsl:apply-templates />       <!-- append utility templates -->       <xsl:copy-of             select="document('')/*/xsl:template                                   [@mode='trace:getCurrent' or                                     @name='trace:getPath']" />       <!-- compute the lowest priority and add a default template with             a lower priority for element nodes -->       <xsl:variable name="priority"                      select="xsl:template/@priority                             [not(. &gt; current( )/xsl:template/@priority)]" />       <xsl:variable name="newpri">         <xsl:choose>           <xsl:when test="$priority &lt; -1">             <xsl:value-of select="$priority - 1" />           </xsl:when>           <!-- in case there's only a greater or no priority at all -->           <xsl:otherwise>-2</xsl:otherwise>          </xsl:choose>       </xsl:variable>       <!-- copy the contents only -->       <alias:template match="*" priority="{$newpri}">         <xsl:copy-of select="document('')/*/xsl:template                              [@name='trace:defaultRule']/node( )" />       </alias:template>     </xsl:copy>   </xsl:template>       <!-- XSLT templates -->   <xsl:template match="xsl:template">     <xsl:copy>       <xsl:copy-of select="@*" />       <!-- first: copy parameters -->       <xsl:apply-templates select="xsl:param" />       <alias:param name="trace:callstack" />       <xsl:choose>         <xsl:when test="@name">           <alias:variable name="trace:current"                           select="concat($trace:callstack,'/{@name}')" />         </xsl:when>         <xsl:otherwise>           <alias:variable name="trace:current"                  select="concat($trace:callstack,                          '/{count(preceding-sibling::xsl:template)+1}')" />         </xsl:otherwise>       </xsl:choose>           <!-- emit a message -->       <alias:message>         <alias:call-template name="trace:getPath" />         <alias:text>&#xA;   stack: </alias:text>         <alias:value-of select="$trace:current" />         <xsl:if test="@match or @mode">           <alias:text> (</alias:text>           <xsl:if test="@match">             <alias:text>match="<xsl:value-of select="@match" />"</alias:text>             <xsl:if test="@mode">               <alias:text><xsl:text> </xsl:text></alias:text>             </xsl:if>           </xsl:if>           <xsl:if test="@mode">             <alias:text>mode="<xsl:value-of select="@mode" />"</alias:text>           </xsl:if>           <alias:text>)</alias:text>         </xsl:if>         <xsl:apply-templates select="xsl:param" mode="traceParams" />       </alias:message>           <!-- process children except parameters -->       <xsl:apply-templates select="node( )[not(self::xsl:param)]" />     </xsl:copy>   </xsl:template>       <!-- add the callstack parameter for apply-templates and call-template -->   <xsl:template match="xsl:apply-templates | xsl:call-template">     <xsl:copy>       <xsl:copy-of select="@*" />       <alias:with-param name="trace:callstack" select="$trace:current" />       <xsl:apply-templates />     </xsl:copy>   </xsl:template>       <!-- output parameter values -->   <xsl:template match="xsl:param" mode="traceParams">     <alias:text>&#xA;   param: name="<xsl:value-of select="@name" />"                          value="</alias:text>     <alias:value-of select="${@name}" />" <alias:text />     <!--     <alias:copy-of select="${@name}" />" <alias:text />     -->   </xsl:template>       <!-- output variable values -->   <xsl:template match="xsl:variable">     <xsl:copy>       <xsl:copy-of select="@*" />       <xsl:apply-templates />     </xsl:copy>     <xsl:if test="ancestor::xsl:template">       <alias:message>   variable: name="<xsl:value-of select="@name" />"                          value="<alias:text />       <alias:value-of select="${@name}" />" </alias:message>     </xsl:if>   </xsl:template>       <!-- copy every unprocessed node -->   <xsl:template match="*|@*">     <xsl:copy>       <xsl:apply-templates select="@*" />       <xsl:apply-templates />     </xsl:copy>   </xsl:template>       <!-- *************************************************************** -->   <!-- The following templates will be copied into the modified        -->   <!-- stylesheet                                                      -->      <!-- *************************************************************** -->       <!--     | trace:getPath    | compute the absolute path of the context node     +-->   <xsl:template name="trace:getPath">     <xsl:text>node: </xsl:text>     <xsl:for-each select="ancestor::*">       <xsl:value-of             select="concat('/', name( ), '[',             count(preceding-sibling::*[name( )=name(current( ))])+1, ']')" />     </xsl:for-each>           <xsl:apply-templates select="." mode="trace:getCurrent" />   </xsl:template>       <!--     | trace:getCurrent    | compute the last step of the location path, depending on the    | node type    +-->   <xsl:template match="*" mode="trace:getCurrent">     <xsl:value-of           select="concat('/', name( ), '[',           count(preceding-sibling::*[name( )=name(current( ))])+1, ']')" />   </xsl:template>       <xsl:template match="@*" mode="trace:getCurrent">     <xsl:value-of select="concat('/@', name( ))" />   </xsl:template>       <xsl:template match="text( )" mode="trace:getCurrent">     <xsl:value-of           select="concat('/text( )[', count(preceding-sibling::text( ))+1,                                                                    ']')" />   </xsl:template>       <xsl:template match="comment( )" mode="trace:getCurrent">     <xsl:value-of           select="concat('/comment( )[',                          count(preceding-sibling::comment( ))+1, ']')" />   </xsl:template>       <xsl:template match="processing-instruction( )" mode="trace:getCurrent">     <xsl:value-of           select="concat('/processing-instruction( )[',           count(preceding-sibling::processing-instruction( ))+1, ']')" />   </xsl:template>       <!--     | trace:defaultRule    | default rule with parameter passing     +-->   <xsl:template name="trace:defaultRule">     <xsl:param name="trace:callstack" />     <xsl:message>       <xsl:call-template name="trace:getPath" />       <xsl:text>&#xA;   default rule applied</xsl:text>     </xsl:message>     <xsl:apply-templates>       <xsl:with-param name="trace:callstack" select="$trace:callstack" />     </xsl:apply-templates>   </xsl:template>     </xsl:transform>

Discussion

Here is a sample of the debug output produced when this transformation was applied to postorder.orgchart.xslt from Recipe 5.5:

node: /employee[1]    stack: /1 (match="/employee") node: /employee[1]/employee[1]    stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[1]/employee[1]    stack: /1/2/3 (match="employee") node: /employee[1]/employee[1]/employee[2]    stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[1]/employee[2]/employee[1]    stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[1]/employee[2]    stack: /1/2/2/reportsTo node: /employee[1]/employee[1]/employee[2]    stack: /1/2/2/HimHer node: /employee[1]/employee[1]    stack: /1/2/reportsTo node: /employee[1]/employee[1]    stack: /1/2/HimHer node: /employee[1]/employee[2]    stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[2]/employee[1]    stack: /1/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]    stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[2]/employee[2]/employee[1]    stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]/employee[2]    stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]/employee[3]    stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]    stack: /1/2/2/reportsTo node: /employee[1]/employee[2]/employee[2]    stack: /1/2/2/HimHer node: /employee[1]/employee[2]    stack: /1/2/reportsTo node: /employee[1]/employee[2]    stack: /1/2/HimHer node: /employee[1]/employee[3]    stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[1]    stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[1]/employee[1]    stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]/employee[2]    stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]/employee[3]    stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]    stack: /1/2/2/reportsTo node: /employee[1]/employee[3]/employee[1]    stack: /1/2/2/HimHer node: /employee[1]/employee[3]/employee[2]    stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[1]    stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[1]/employee[1]    stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[1]    stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[1]    stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[2]    stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[1]    stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[2]    stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2]    stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[2]    stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[3]    stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[3]/employee[1]    stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[3]    stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[3]    stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]    stack: /1/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]    stack: /1/2/2/HimHer node: /employee[1]/employee[3]    stack: /1/2/reportsTo node: /employee[1]/employee[3]    stack: /1/2/HimHer node: /employee[1]    stack: /1/reportsTo node: /employee[1]    stack: /1/HimHer

The modified stylesheet outputs trace messages via the xsl:message mechanism. The format for every processed node is as follows:

node: [XPath to this node]    stack: [call stack of the templates invoked]    param: name="[parameter name]" value="[parameter value]"    more parameters ...    variable: name="[variable name]" value="[variable value]"    more variables ...

The call stack takes the form of a path (with / as separator) and includes all passed templates. If a template has a name attribute, then this name is used. Otherwise, the number (position) of the template appears within the stack. If the current template does not have a name, the match attribute is displayed. If a mode attribute is specified, its value is displayed.

One known problem is that the output for parameters or variables is their string value (produced with xsl:value-of). That's not reasonable for node sets and result-tree fragments. However, using xsl:copy-of results in an error if the variable contains attribute or namespace nodes without parents.

See Also

The trace.xslt source and further examples can be found at http://www.informatik.hu-berlin.de/~obecker/XSLT/#trace.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net