Einfügen von Debug-Ausgaben automatisieren
(Auszug aus "XSLT Kochbuch" von Sal Mangano)
Problem
Sie möchten Ihr Stylesheet in ein anderes Stylesheet transformieren, das mit Debug-Traces angereichert ist.
Lösung
Oliver Becker hat eine praktische Stylesheet-Transformation entwickelt, die aus einem beliebigen Eingabe-Stylesheet ein Ausgabe-Stylesheet mit Trace-Aufrufen erzeugt:
<!--
Trace-Utility; modifiziert ein Stylesheet so, dass es Trace-Meldungen produziert
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-Wurzelelement -->
<xsl:template match="xsl:stylesheet | xsl:transform">
<xsl:copy>
<!-- Wir brauchen den trace-Namensraum für Namen und Modi -->
<xsl:copy-of select="document('')/*/namespace::trace" />
<!-- dito: Vielleicht wurde ein Namensraum nur als Attributwert benutzt -->
<xsl:copy-of select="namespace::*|@*" />
<xsl:apply-templates />
<!-- hänge Hilfs-Templates an -->
<xsl:copy-of select="document('')/*/xsl:template[@mode='trace:getCurrent' or @name='trace:getPath']" />
<!-- berechne geringste Priorität, und füge ein Standard-Template mit einer niedrigeren Priorität für Elementknoten hinzu -->
<xsl:variable name="priority" select="xsl:template/@priority[not(. > current( )/xsl:template/@priority)]" />
<xsl:variable name="newpri">
<xsl:choose>
<xsl:when test="$priority < −1">
<xsl:value-of select="$priority - 1" />
</xsl:when>
<!-- falls es nur eine größere oder gar keine Priorität gibt -->
<xsl:otherwise>-2</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- kopiere nur die Inhalte -->
<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="@*" />
<!-- zuerst: Parameter kopieren -->
<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>
<!-- gib eine Meldung aus -->
<alias:message>
<alias:call-template name="trace:getPath" />
<alias:text>
 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>
<!-- bearbeite Kinder außer Parametern -->
<xsl:apply-templates select="node( )[not(self::xsl:param)]" />
</xsl:copy>
</xsl:template>
<!-- füge callstack-Parameter für apply-templates und call-template hinzu -->
<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>
<!-- gib Parameterwerte aus -->
<xsl:template match="xsl:param" mode="traceParams">
<alias:text>
 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>
<!-- gibt Variablenwerte aus -->
<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>
<!-- kopiere alle unbearbeiteten Knoten -->
<xsl:template match="*|@*">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- ************************************************************************ -->
<!-- Die folgenden Templates werden in das modifizierte Stylesheet kopiert. -->
<!-- ************************************************************************ -->
<!--
| trace:getPath
| berechne den absoluten Pfad des Kontextknotens
+ -->
<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
| berechne den letzten Schritt des Lokalisierungspfades
| je nach Knotentyp
+ -->
<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
| Standardregel mit Parameterübergabe
+ -->
<xsl:template name="trace:defaultRule">
<xsl:param name="trace:callstack" />
<xsl:message>
<xsl:call-template name="trace:getPath" />
<xsl:text>
 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>
Diskussion
Betrachten Sie das folgende Beispiel einer Debug-Ausgabe, die sich ergibt, wenn diese Transformation auf postorder.orgchart.xslt aus dem Rezept Durchführen eines Postorder Traversal angewendet wird:
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
Das modifizierte Stylesheet gibt Trace-Meldungen mit Hilfe des xsl:message-Mechanismus aus. Dabei sieht das Format bei allen verarbeiteten Knoten wie folgt aus:
node: [XPath zu diesem Knoten]
stack: [Aufruf-Stack der aufgerufenen Templates]
param: name="[Parametername]" value="[Parameterwert]"
weitere Parameter ...
variable: name="[Variablenname]" value="[Variablenwert]"
weitere Variablen ...
Der Aufruf-Stack hat die Form eines Pfades (mit / als Trennzeichen) und umfasst alle übergebenen Templates. Falls ein Template über ein name-Attribut verfügt, dann wird dieser Name verwendet. Ansonsten erscheint die Nummer (die Position) des Templates im Stack. Sollte das aktuelle Template keinen Namen haben, dann wird das match-Attribut angezeigt. Falls ein mode-Attribut angegeben ist, dann wird dessen Wert angezeigt.
Ein bekanntes Problem besteht darin, dass bei Parametern oder Variablen die Ausgabe in deren String-Wert besteht (der mit xsl:value-of erzeugt wird). Bei Knotenmengen und Teilen von Ergebnisbäumen macht das keinen Sinn. Allerdings führt ein xsl:copy-of zu einem Fehler, falls die Variable elternlose Attribut- oder Namensraumknoten enthält.
Siehe auch
Den Quellcode von trace.xslt sowie weitere Beispiele finden Sie unter Oliver's XSLT page.
<< zurück | vor >> |
Tipp der data2type-Redaktion: Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an: |
Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.
O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de