phax / ph-schematron

Java Schematron library that supports XSLT and native application
Apache License 2.0
115 stars 36 forks source link

Invalid xslt when using maven plugin #174

Closed oliverunger closed 3 months ago

oliverunger commented 3 months ago

image image

Plugin:

<build>
        <plugins>
            <plugin>
                <groupId>com.helger.maven</groupId>
                <artifactId>ph-schematron-maven-plugin</artifactId>
                <version>8.0.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>convert</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <schematronDirectory>${basedir}/src/main/resources/rules/examples/schematron/arche</schematronDirectory>
                    <xsltDirectory>${project.build.directory}/generated-sources/schematron</xsltDirectory>
                    <schematronProcessingEngine>schematron</schematronProcessingEngine>
                </configuration>
            </plugin>
        </plugins>
    </build>

arche.sch

<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
    <sch:ns prefix="arc" uri="http://www.schematron.info/arche"/>

    <!-- Übungsaufgaben -->
    <!-- Regel 1 (Hier muss das Geschlecht unterschieden werden, da die Grenzen bei den Tierarten einzeln aufgefuehrt sind!) -->
    <sch:pattern>
        <sch:rule context="arc:tier[@geschlecht='männlich']">
            <sch:report test="number(arc:alter) &gt; number(//arc:maxReproduktionsalter/arc:tier_art[arc:name=current()/arc:art]/arc:männlich)">
                Ein männliches Tier darf nicht das artenspezifische Reproduktionsalter überschritten haben.
            </sch:report>
        </sch:rule>
        <sch:rule context="arc:tier[@geschlecht='weiblich']">
            <sch:report test="number(arc:alter) &gt; number(//arc:maxReproduktionsalter/arc:tier_art[arc:name=current()/arc:art]/arc:weiblich)">
                Ein weibliches Tier darf nicht das artenspezifische Reproduktionsalter überschritten haben.
            </sch:report>
        </sch:rule>
    </sch:pattern>

    <!-- Regel 2 -->
    <sch:pattern>
        <sch:rule context="arc:nutzlast">
            <sch:report test=". &lt; sum(//arc:gewicht)">
                Die Nutzlast der Arche darf nicht überschritten werden.
            </sch:report>
        </sch:rule>
    </sch:pattern>

    <!-- Regel 3 -->
    <sch:pattern>
        <sch:rule context="arc:tier[@fleischfresser='ja']">
            <sch:report test="parent::*/arc:tier[@fleischfresser='nein']">
                Pflanzen- und Fleischfresser dürfen nicht in einem Zimmer untergebracht werden.
            </sch:report>
        </sch:rule>
    </sch:pattern>

    <!-- Regel 4 -->
    <sch:pattern>
        <sch:rule context="arc:tier[@fleischfresser='ja']">
            <sch:report test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 2)">
                Ein fleischfressendes Tier darf maximal doppelt so schwer ein Zimmergenosse.
            </sch:report>
        </sch:rule>
    </sch:pattern>

    <!-- Regel 5 -->
    <sch:pattern>
        <sch:rule context="arc:tier[@fleischfresser='nein']">
            <sch:report test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 10)">
                Ein pflanzenfressendes Tier darf maximal zehnmal so schwer sein wie ein anderer Zimmergenosse.
            </sch:report>
        </sch:rule>
    </sch:pattern>

    <!-- Regel 6 -->
    <sch:pattern>
        <sch:rule context="arc:tier_art">
            <sch:report test="not(//arc:tier[arc:art = current()/arc:name])">
                Von jeder Gattung muss ein Tier vorhanden sein.
            </sch:report>
        </sch:rule>
    </sch:pattern>

    <!--
        Regel 7 Es muss genau ein Paar jeder Gattung auf der Arche untergebracht werden.
        Regel 8 Das Paar muss auch im gleichen Zimmer untergebracht werden.
     -->
    <sch:pattern>
        <sch:rule context="arc:tier">
            <sch:report test="count(//arc:tier[arc:art=current()/arc:art]) &gt; 2">
                In der Arche gibt es mehr als zwei Tiere dieser Art.
            </sch:report>
            <sch:report test="count(parent::*/arc:tier[arc:art=current()/arc:art]) &lt; 2">
                In diesem Zimmer gibt es weniger als zwei Tiere dieser Art.
            </sch:report>
            <sch:assert test="count(parent::*/arc:tier[arc:art=current()/arc:art][@geschlecht='männlich']) = 1">
                Ein Paar muss immer aus einem Männchen und einem Weibchen bestehen.
            </sch:assert>
        </sch:rule>
    </sch:pattern>

    <!-- Regel 9 -->
    <sch:pattern>
        <sch:rule context="arc:zimmer">
            <sch:report test="count(arc:tier) &gt; 6">
                In einem Zimmer dürfen nicht mehr als sechs Tiere untergebracht werden.
            </sch:report>
        </sch:rule>
    </sch:pattern>
</sch:schema>

XSLT

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet xmlns:svrl="http://purl.oclc.org/dsdl/svrl" xmlns:arc="http://www.schematron.info/arche" xmlns:iso="http://purl.oclc.org/dsdl/schematron" xmlns:saxon="http://saxon.sf.net/" xmlns:schold="http://www.ascc.net/xml/schematron" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<!--Implementers: please note that overriding process-prolog or process-root is 
    the preferred method for meta-stylesheets to use where possible. -->

<xsl:param name="archiveDirParameter" />
  <xsl:param name="archiveNameParameter" />
  <xsl:param name="fileNameParameter" />
  <xsl:param name="fileDirParameter" />
  <xsl:variable name="document-uri">
    <xsl:value-of select="document-uri(/)" />
  </xsl:variable>

<!--PHASES-->

<!--PROLOG-->
<xsl:output indent="yes" method="xml" omit-xml-declaration="no" standalone="yes" />

<!--XSD TYPES FOR XSLT2-->

<!--KEYS AND FUNCTIONS-->

<!--DEFAULT RULES-->

<!--MODE: SCHEMATRON-SELECT-FULL-PATH-->
<!--This mode can be used to generate an ugly though full XPath for locators-->
<xsl:template match="*" mode="schematron-select-full-path">
    <xsl:apply-templates mode="schematron-get-full-path" select="." />
  </xsl:template>

<!--MODE: SCHEMATRON-FULL-PATH-->
<!--This mode can be used to generate an ugly though full XPath for locators-->
<xsl:template match="*" mode="schematron-get-full-path">
    <xsl:apply-templates mode="schematron-get-full-path" select="parent::*" />
    <xsl:text>/</xsl:text>
    <xsl:choose>
      <xsl:when test="namespace-uri()=''">
        <xsl:value-of select="name()" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>*:</xsl:text>
        <xsl:value-of select="local-name()" />
        <xsl:text>[namespace-uri()='</xsl:text>
        <xsl:value-of select="namespace-uri()" />
        <xsl:text>']</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:variable name="preceding" select="count(preceding-sibling::*[local-name()=local-name(current())                                   and namespace-uri() = namespace-uri(current())])" />
    <xsl:text>[</xsl:text>
    <xsl:value-of select="1+ $preceding" />
    <xsl:text>]</xsl:text>
  </xsl:template>
  <xsl:template match="@*" mode="schematron-get-full-path">
    <xsl:apply-templates mode="schematron-get-full-path" select="parent::*" />
    <xsl:text>/</xsl:text>
    <xsl:choose>
      <xsl:when test="namespace-uri()=''">@<xsl:value-of select="name()" />
</xsl:when>
      <xsl:otherwise>
        <xsl:text>@*[local-name()='</xsl:text>
        <xsl:value-of select="local-name()" />
        <xsl:text>' and namespace-uri()='</xsl:text>
        <xsl:value-of select="namespace-uri()" />
        <xsl:text>']</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

<!--MODE: SCHEMATRON-FULL-PATH-2-->
<!--This mode can be used to generate prefixed XPath for humans-->
<xsl:template match="node() | @*" mode="schematron-get-full-path-2">
    <xsl:for-each select="ancestor-or-self::*">
      <xsl:text>/</xsl:text>
      <xsl:value-of select="name(.)" />
      <xsl:if test="preceding-sibling::*[name(.)=name(current())]">
        <xsl:text>[</xsl:text>
        <xsl:value-of select="count(preceding-sibling::*[name(.)=name(current())])+1" />
        <xsl:text>]</xsl:text>
      </xsl:if>
    </xsl:for-each>
    <xsl:if test="not(self::*)">
      <xsl:text />/@<xsl:value-of select="name(.)" />
    </xsl:if>
  </xsl:template>
<!--MODE: SCHEMATRON-FULL-PATH-3-->
<!--This mode can be used to generate prefixed XPath for humans 
    (Top-level element has index)-->

<xsl:template match="node() | @*" mode="schematron-get-full-path-3">
    <xsl:for-each select="ancestor-or-self::*">
      <xsl:text>/</xsl:text>
      <xsl:value-of select="name(.)" />
      <xsl:if test="parent::*">
        <xsl:text>[</xsl:text>
        <xsl:value-of select="count(preceding-sibling::*[name(.)=name(current())])+1" />
        <xsl:text>]</xsl:text>
      </xsl:if>
    </xsl:for-each>
    <xsl:if test="not(self::*)">
      <xsl:text />/@<xsl:value-of select="name(.)" />
    </xsl:if>
  </xsl:template>

<!--MODE: GENERATE-ID-FROM-PATH -->
<xsl:template match="/" mode="generate-id-from-path" />
  <xsl:template match="text()" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.text-', 1+count(preceding-sibling::text()), '-')" />
  </xsl:template>
  <xsl:template match="comment()" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.comment-', 1+count(preceding-sibling::comment()), '-')" />
  </xsl:template>
  <xsl:template match="processing-instruction()" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.processing-instruction-', 1+count(preceding-sibling::processing-instruction()), '-')" />
  </xsl:template>
  <xsl:template match="@*" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.@', name())" />
  </xsl:template>
  <xsl:template match="*" mode="generate-id-from-path" priority="-0.5">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:text>.</xsl:text>
    <xsl:value-of select="concat('.',name(),'-',1+count(preceding-sibling::*[name()=name(current())]),'-')" />
  </xsl:template>

<!--MODE: GENERATE-ID-2 -->
<xsl:template match="/" mode="generate-id-2">U</xsl:template>
  <xsl:template match="*" mode="generate-id-2" priority="2">
    <xsl:text>U</xsl:text>
    <xsl:number count="*" level="multiple" />
  </xsl:template>
  <xsl:template match="node()" mode="generate-id-2">
    <xsl:text>U.</xsl:text>
    <xsl:number count="*" level="multiple" />
    <xsl:text>n</xsl:text>
    <xsl:number count="node()" />
  </xsl:template>
  <xsl:template match="@*" mode="generate-id-2">
    <xsl:text>U.</xsl:text>
    <xsl:number count="*" level="multiple" />
    <xsl:text>_</xsl:text>
    <xsl:value-of select="string-length(local-name(.))" />
    <xsl:text>_</xsl:text>
    <xsl:value-of select="translate(name(),':','.')" />
  </xsl:template>
<!--Strip characters-->  <xsl:template match="text()" priority="-1" />

<!--SCHEMA SETUP-->
<xsl:template match="/">
    <svrl:schematron-output schemaVersion="" title="">
      <xsl:comment>
        <xsl:value-of select="$archiveDirParameter" />   
         <xsl:value-of select="$archiveNameParameter" />  
         <xsl:value-of select="$fileNameParameter" />  
         <xsl:value-of select="$fileDirParameter" />
      </xsl:comment>
      <svrl:ns-prefix-in-attribute-values prefix="arc" uri="http://www.schematron.info/arche" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M1" select="/" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M2" select="/" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M3" select="/" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M4" select="/" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M5" select="/" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M6" select="/" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M7" select="/" />
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M8" select="/" />
    </svrl:schematron-output>
  </xsl:template>

<!--SCHEMATRON PATTERNS-->

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:tier[@geschlecht='männlich']" mode="M1" priority="1001">
    <svrl:fired-rule context="arc:tier[@geschlecht='männlich']" />

        <!--REPORT -->
<xsl:if test="number(arc:alter) > number(//arc:maxReproduktionsalter/arc:tier_art[arc:name=current()/arc:art]/arc:männlich)">
      <svrl:successful-report test="number(arc:alter) > number(//arc:maxReproduktionsalter/arc:tier_art[arc:name=current()/arc:art]/arc:männlich)">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                Ein männliches Tier darf nicht das artenspezifische Reproduktionsalter überschritten haben.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M1" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

    <!--RULE -->
<xsl:template match="arc:tier[@geschlecht='weiblich']" mode="M1" priority="1000">
    <svrl:fired-rule context="arc:tier[@geschlecht='weiblich']" />

        <!--REPORT -->
<xsl:if test="number(arc:alter) > number(//arc:maxReproduktionsalter/arc:tier_art[arc:name=current()/arc:art]/arc:weiblich)">
      <svrl:successful-report test="number(arc:alter) > number(//arc:maxReproduktionsalter/arc:tier_art[arc:name=current()/arc:art]/arc:weiblich)">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                Ein weibliches Tier darf nicht das artenspezifische Reproduktionsalter überschritten haben.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M1" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M1" priority="-1" />
  <xsl:template match="@*|node()" mode="M1" priority="-2">
    <xsl:apply-templates mode="M1" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:nutzlast" mode="M2" priority="1000">
    <svrl:fired-rule context="arc:nutzlast" />

        <!--REPORT -->
<xsl:if test=". &lt; sum(//arc:gewicht)">
      <svrl:successful-report test=". &lt; sum(//arc:gewicht)">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                Die Nutzlast der Arche darf nicht überschritten werden.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M2" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M2" priority="-1" />
  <xsl:template match="@*|node()" mode="M2" priority="-2">
    <xsl:apply-templates mode="M2" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:tier[@fleischfresser='ja']" mode="M3" priority="1000">
    <svrl:fired-rule context="arc:tier[@fleischfresser='ja']" />

        <!--REPORT -->
<xsl:if test="parent::*/arc:tier[@fleischfresser='nein']">
      <svrl:successful-report test="parent::*/arc:tier[@fleischfresser='nein']">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                Pflanzen- und Fleischfresser dürfen nicht in einem Zimmer untergebracht werden.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M3" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M3" priority="-1" />
  <xsl:template match="@*|node()" mode="M3" priority="-2">
    <xsl:apply-templates mode="M3" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:tier[@fleischfresser='ja']" mode="M4" priority="1000">
    <svrl:fired-rule context="arc:tier[@fleischfresser='ja']" />

        <!--REPORT -->
<xsl:if test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 2)">
      <svrl:successful-report test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 2)">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                Ein fleischfressendes Tier darf maximal doppelt so schwer ein Zimmergenosse.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M4" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M4" priority="-1" />
  <xsl:template match="@*|node()" mode="M4" priority="-2">
    <xsl:apply-templates mode="M4" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:tier[@fleischfresser='nein']" mode="M5" priority="1000">
    <svrl:fired-rule context="arc:tier[@fleischfresser='nein']" />

        <!--REPORT -->
<xsl:if test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 10)">
      <svrl:successful-report test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 10)">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                Ein pflanzenfressendes Tier darf maximal zehnmal so schwer sein wie ein anderer Zimmergenosse.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M5" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M5" priority="-1" />
  <xsl:template match="@*|node()" mode="M5" priority="-2">
    <xsl:apply-templates mode="M5" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:tier_art" mode="M6" priority="1000">
    <svrl:fired-rule context="arc:tier_art" />

        <!--REPORT -->
<xsl:if test="not(//arc:tier[arc:art = current()/arc:name])">
      <svrl:successful-report test="not(//arc:tier[arc:art = current()/arc:name])">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                Von jeder Gattung muss ein Tier vorhanden sein.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M6" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M6" priority="-1" />
  <xsl:template match="@*|node()" mode="M6" priority="-2">
    <xsl:apply-templates mode="M6" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:tier" mode="M7" priority="1000">
    <svrl:fired-rule context="arc:tier" />

        <!--REPORT -->
<xsl:if test="count(//arc:tier[arc:art=current()/arc:art]) > 2">
      <svrl:successful-report test="count(//arc:tier[arc:art=current()/arc:art]) > 2">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                In der Arche gibt es mehr als zwei Tiere dieser Art.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>

        <!--REPORT -->
<xsl:if test="count(parent::*/arc:tier[arc:art=current()/arc:art]) &lt; 2">
      <svrl:successful-report test="count(parent::*/arc:tier[arc:art=current()/arc:art]) &lt; 2">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                In diesem Zimmer gibt es weniger als zwei Tiere dieser Art.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>

        <!--ASSERT -->
<xsl:choose>
      <xsl:when test="count(parent::*/arc:tier[arc:art=current()/arc:art][@geschlecht='männlich']) = 1" />
      <xsl:otherwise>
        <svrl:failed-assert test="count(parent::*/arc:tier[arc:art=current()/arc:art][@geschlecht='männlich']) = 1">
          <xsl:attribute name="location">
            <xsl:apply-templates mode="schematron-select-full-path" select="." />
          </xsl:attribute>
          <svrl:text>
                Ein Paar muss immer aus einem Männchen und einem Weibchen bestehen.
            </svrl:text>
        </svrl:failed-assert>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:apply-templates mode="M7" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M7" priority="-1" />
  <xsl:template match="@*|node()" mode="M7" priority="-2">
    <xsl:apply-templates mode="M7" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="arc:zimmer" mode="M8" priority="1000">
    <svrl:fired-rule context="arc:zimmer" />

        <!--REPORT -->
<xsl:if test="count(arc:tier) > 6">
      <svrl:successful-report test="count(arc:tier) > 6">
        <xsl:attribute name="location">
          <xsl:apply-templates mode="schematron-select-full-path" select="." />
        </xsl:attribute>
        <svrl:text>
                In einem Zimmer dürfen nicht mehr als sechs Tiere untergebracht werden.
            </svrl:text>
      </svrl:successful-report>
    </xsl:if>
    <xsl:apply-templates mode="M8" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M8" priority="-1" />
  <xsl:template match="@*|node()" mode="M8" priority="-2">
    <xsl:apply-templates mode="M8" select="@*|*|comment()|processing-instruction()" />
  </xsl:template>
</xsl:stylesheet>
oliverunger commented 3 months ago

Is this the right way to check validation at runtime against the transpiled xslt?

  final File preCompiledXSLT = ResourceUtils.getFile("classpath:arche.xslt");
        ISchematronXSLTBasedResource xslt = SchematronResourceSchXslt_XSLT2.fromFile(preCompiledXSLT);
        if(!xslt.isValidSchematron()) {
            throw new IllegalArgumentException("Das XSLT-Dokument ist nicht gültig!");
        }

        // XML-Dokument laden
        final Document aXML = DOMReader.readXMLDOM(xml);
        if (aXML == null) {
            throw new IllegalArgumentException("Das XML-Dokument ist nicht gültig!");
        }

        // Validierung durchführen
//        final SchematronOutputType schematronOutputType = aResSCH.applySchematronValidationToSVRL(new DOMSource(aXML));
        final SchematronOutputType schematronOutputType = xslt.applySchematronValidationToSVRL(new DOMSource(aXML));
        if (schematronOutputType == null) {
            throw new IllegalArgumentException("Fehler bei der Anwendung der Schematron-Validierung!");
        }

        return schematronOutputType;
phax commented 3 months ago

So the created XSLT is totally fine. The code snippet you are highlighting is about getting the name of an element. And that makes a difference, if the namespace URI is the default one (empty string) or not:

    <xsl:choose>
      <xsl:when test="namespace-uri()=''">
        <xsl:value-of select="name()" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>*:</xsl:text>
        <xsl:value-of select="local-name()" />
        <xsl:text>[namespace-uri()='</xsl:text>
        <xsl:value-of select="namespace-uri()" />
        <xsl:text>']</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

And yes, the code looks good. Even though I suggest instead of wrapping it in a DOMSource try this:

 final SchematronOutputType schematronOutputType = xslt.applySchematronValidationToSVRL(aXML, (String) null);
        if (schematronOutputType == null) {
            throw new IllegalArgumentException("Fehler bei der Anwendung der Schematron-Validierung!");
        }

the 2nd base URI parameter is relevant when you have includes to resolve etc.

oliverunger commented 3 months ago

Still have this issue: 17:44:38.756 [main] ERROR com.helger.xml.transform.LoggingTransformErrorListener -- [fatal_error] Transformation fatal error (net.sf.saxon.trans.XPathException: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: Es wurde versucht, einen Knoten an einer Stelle einzufügen, an der dies nicht zulässig ist.) 17:44:38.760 [main] ERROR com.helger.schematron.schxslt.xslt2.SchematronProviderXSLTFromSchXslt_XSLT2 -- SchXslt preprocessor error net.sf.saxon.trans.XPathException: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: Es wurde versucht, einen Knoten an einer Stelle einzufügen, an der dies nicht zulässig ist.

Translation from german: It was tried to add an element at an invalid position.

Appears in this line: final SchematronOutputType schematronOutputType = xslt.applySchematronValidationToSVRL(aXML, null);

phax commented 3 months ago

I speak German so all good :) Can you paste an example XML here as well please?

oliverunger commented 3 months ago

Just so that the others can understand.

This should be a valid one:

<?xml version="1.0" encoding="UTF-8"?>
<arche xmlns="http://www.schematron.info/arche"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.schematron.info/arche arche.xsd">
    <ladung>
        <zimmer>
            <tier geschlecht="weiblich" fleischfresser="nein">
                <art>Zebra</art>
                <gewicht>200</gewicht>
                <alter>12</alter>
            </tier>
            <tier geschlecht="männlich" fleischfresser="nein">
                <art>Zebra</art>
                <gewicht>250</gewicht>
                <alter>13</alter>
            </tier>
        </zimmer>
        <zimmer>
            <tier geschlecht="weiblich" fleischfresser="ja">
                <art>Löwe</art>
                <gewicht>200</gewicht>
                <alter>14</alter>
            </tier>
            <tier geschlecht="männlich" fleischfresser="ja">
                <art>Löwe</art>
                <gewicht>180</gewicht>
                <alter>30</alter>
            </tier>
        </zimmer>
        <zimmer>
            <tier geschlecht="weiblich" fleischfresser="nein">
                <art>Elefant</art>
                <gewicht>10000</gewicht>
                <alter>20</alter>
            </tier>
            <tier geschlecht="männlich" fleischfresser="nein">
                <art>Elefant</art>
                <gewicht>15000</gewicht>
                <alter>40</alter>
            </tier>
        </zimmer>
    </ladung>
    <maxReproduktionsalter>
        <tier_art>
            <name>Elefant</name>
            <männlich>80</männlich>
            <weiblich>30</weiblich>
        </tier_art>
        <tier_art>
            <name>Löwe</name>
            <männlich>30</männlich>
            <weiblich>15</weiblich>
        </tier_art>
        <tier_art>
            <name>Zebra</name>
            <männlich>30</männlich>
            <weiblich>20</weiblich>
        </tier_art>
    </maxReproduktionsalter>
    <nutzlast>44000</nutzlast>
</arche>

This one should violate rule 1:

<?xml version="1.0" encoding="UTF-8"?>
<arche xmlns="http://www.schematron.info/arche"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.schematron.info/arche arche.xsd">
    <ladung>
        <zimmer>
            <tier geschlecht="weiblich" fleischfresser="nein">
                <art>Zebra</art>
                <gewicht>200</gewicht>
                <alter>21</alter>
            </tier>
            <tier geschlecht="männlich" fleischfresser="nein">
                <art>Zebra</art>
                <gewicht>250</gewicht>
                <alter>13</alter>
            </tier>
        </zimmer>
        <zimmer>
            <tier geschlecht="weiblich" fleischfresser="ja">
                <art>Löwe</art>
                <gewicht>200</gewicht>
                <alter>14</alter>
            </tier>
            <tier geschlecht="männlich" fleischfresser="ja">
                <art>Löwe</art>
                <gewicht>180</gewicht>
                <alter>30</alter>
            </tier>
        </zimmer>
        <zimmer>
            <tier geschlecht="weiblich" fleischfresser="nein">
                <art>Elefant</art>
                <gewicht>10000</gewicht>
                <alter>20</alter>
            </tier>
            <tier geschlecht="männlich" fleischfresser="nein">
                <art>Elefant</art>
                <gewicht>15000</gewicht>
                <alter>40</alter>
            </tier>
        </zimmer>
    </ladung>
    <maxReproduktionsalter>
        <tier_art>
            <name>Elefant</name>
            <männlich>80</männlich>
            <weiblich>30</weiblich>
        </tier_art>
        <tier_art>
            <name>Löwe</name>
            <männlich>30</männlich>
            <weiblich>15</weiblich>
        </tier_art>
        <tier_art>
            <name>Zebra</name>
            <männlich>30</männlich>
            <weiblich>20</weiblich>
        </tier_art>
    </maxReproduktionsalter>
    <nutzlast>44000</nutzlast>
</arche>
oliverunger commented 3 months ago

This very simple one does not work either:

SCH:

<?xml version="1.0" encoding="utf-8"?>
<schema xmlns="http://purl.oclc.org/dsdl/schematron"
            queryBinding="xslt2">
 <pattern>
     <rule context="ID">
         <assert test="string-length(.) &gt; 9">Something with chars.</assert>
     </rule>
 </pattern>

</schema>

Transpiled:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet xmlns:svrl="http://purl.oclc.org/dsdl/svrl" xmlns:iso="http://purl.oclc.org/dsdl/schematron" xmlns:saxon="http://saxon.sf.net/" xmlns:schold="http://www.ascc.net/xml/schematron" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<!--Implementers: please note that overriding process-prolog or process-root is 
    the preferred method for meta-stylesheets to use where possible. -->

<xsl:param name="archiveDirParameter" />
  <xsl:param name="archiveNameParameter" />
  <xsl:param name="fileNameParameter" />
  <xsl:param name="fileDirParameter" />
  <xsl:variable name="document-uri">
    <xsl:value-of select="document-uri(/)" />
  </xsl:variable>

<!--PHASES-->

<!--PROLOG-->
<xsl:output indent="yes" method="xml" omit-xml-declaration="no" standalone="yes" />

<!--XSD TYPES FOR XSLT2-->

<!--KEYS AND FUNCTIONS-->

<!--DEFAULT RULES-->

<!--MODE: SCHEMATRON-SELECT-FULL-PATH-->
<!--This mode can be used to generate an ugly though full XPath for locators-->
<xsl:template match="*" mode="schematron-select-full-path">
    <xsl:apply-templates mode="schematron-get-full-path" select="." />
  </xsl:template>

<!--MODE: SCHEMATRON-FULL-PATH-->
<!--This mode can be used to generate an ugly though full XPath for locators-->
<xsl:template match="*" mode="schematron-get-full-path">
    <xsl:apply-templates mode="schematron-get-full-path" select="parent::*" />
    <xsl:text>/</xsl:text>
    <xsl:choose>
      <xsl:when test="namespace-uri()=''">
        <xsl:value-of select="name()" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>*:</xsl:text>
        <xsl:value-of select="local-name()" />
        <xsl:text>[namespace-uri()='</xsl:text>
        <xsl:value-of select="namespace-uri()" />
        <xsl:text>']</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:variable name="preceding" select="count(preceding-sibling::*[local-name()=local-name(current())                                   and namespace-uri() = namespace-uri(current())])" />
    <xsl:text>[</xsl:text>
    <xsl:value-of select="1+ $preceding" />
    <xsl:text>]</xsl:text>
  </xsl:template>
  <xsl:template match="@*" mode="schematron-get-full-path">
    <xsl:apply-templates mode="schematron-get-full-path" select="parent::*" />
    <xsl:text>/</xsl:text>
    <xsl:choose>
      <xsl:when test="namespace-uri()=''">@<xsl:value-of select="name()" />
</xsl:when>
      <xsl:otherwise>
        <xsl:text>@*[local-name()='</xsl:text>
        <xsl:value-of select="local-name()" />
        <xsl:text>' and namespace-uri()='</xsl:text>
        <xsl:value-of select="namespace-uri()" />
        <xsl:text>']</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

<!--MODE: SCHEMATRON-FULL-PATH-2-->
<!--This mode can be used to generate prefixed XPath for humans-->
<xsl:template match="node() | @*" mode="schematron-get-full-path-2">
    <xsl:for-each select="ancestor-or-self::*">
      <xsl:text>/</xsl:text>
      <xsl:value-of select="name(.)" />
      <xsl:if test="preceding-sibling::*[name(.)=name(current())]">
        <xsl:text>[</xsl:text>
        <xsl:value-of select="count(preceding-sibling::*[name(.)=name(current())])+1" />
        <xsl:text>]</xsl:text>
      </xsl:if>
    </xsl:for-each>
    <xsl:if test="not(self::*)">
      <xsl:text />/@<xsl:value-of select="name(.)" />
    </xsl:if>
  </xsl:template>
<!--MODE: SCHEMATRON-FULL-PATH-3-->
<!--This mode can be used to generate prefixed XPath for humans 
    (Top-level element has index)-->

<xsl:template match="node() | @*" mode="schematron-get-full-path-3">
    <xsl:for-each select="ancestor-or-self::*">
      <xsl:text>/</xsl:text>
      <xsl:value-of select="name(.)" />
      <xsl:if test="parent::*">
        <xsl:text>[</xsl:text>
        <xsl:value-of select="count(preceding-sibling::*[name(.)=name(current())])+1" />
        <xsl:text>]</xsl:text>
      </xsl:if>
    </xsl:for-each>
    <xsl:if test="not(self::*)">
      <xsl:text />/@<xsl:value-of select="name(.)" />
    </xsl:if>
  </xsl:template>

<!--MODE: GENERATE-ID-FROM-PATH -->
<xsl:template match="/" mode="generate-id-from-path" />
  <xsl:template match="text()" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.text-', 1+count(preceding-sibling::text()), '-')" />
  </xsl:template>
  <xsl:template match="comment()" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.comment-', 1+count(preceding-sibling::comment()), '-')" />
  </xsl:template>
  <xsl:template match="processing-instruction()" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.processing-instruction-', 1+count(preceding-sibling::processing-instruction()), '-')" />
  </xsl:template>
  <xsl:template match="@*" mode="generate-id-from-path">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:value-of select="concat('.@', name())" />
  </xsl:template>
  <xsl:template match="*" mode="generate-id-from-path" priority="-0.5">
    <xsl:apply-templates mode="generate-id-from-path" select="parent::*" />
    <xsl:text>.</xsl:text>
    <xsl:value-of select="concat('.',name(),'-',1+count(preceding-sibling::*[name()=name(current())]),'-')" />
  </xsl:template>

<!--MODE: GENERATE-ID-2 -->
<xsl:template match="/" mode="generate-id-2">U</xsl:template>
  <xsl:template match="*" mode="generate-id-2" priority="2">
    <xsl:text>U</xsl:text>
    <xsl:number count="*" level="multiple" />
  </xsl:template>
  <xsl:template match="node()" mode="generate-id-2">
    <xsl:text>U.</xsl:text>
    <xsl:number count="*" level="multiple" />
    <xsl:text>n</xsl:text>
    <xsl:number count="node()" />
  </xsl:template>
  <xsl:template match="@*" mode="generate-id-2">
    <xsl:text>U.</xsl:text>
    <xsl:number count="*" level="multiple" />
    <xsl:text>_</xsl:text>
    <xsl:value-of select="string-length(local-name(.))" />
    <xsl:text>_</xsl:text>
    <xsl:value-of select="translate(name(),':','.')" />
  </xsl:template>
<!--Strip characters-->  <xsl:template match="text()" priority="-1" />

<!--SCHEMA SETUP-->
<xsl:template match="/">
    <svrl:schematron-output schemaVersion="" title="">
      <xsl:comment>
        <xsl:value-of select="$archiveDirParameter" />   
         <xsl:value-of select="$archiveNameParameter" />  
         <xsl:value-of select="$fileNameParameter" />  
         <xsl:value-of select="$fileDirParameter" />
      </xsl:comment>
      <svrl:active-pattern>
        <xsl:attribute name="document">
          <xsl:value-of select="document-uri(/)" />
        </xsl:attribute>
        <xsl:apply-templates />
      </svrl:active-pattern>
      <xsl:apply-templates mode="M0" select="/" />
    </svrl:schematron-output>
  </xsl:template>

<!--SCHEMATRON PATTERNS-->

<!--PATTERN -->

    <!--RULE -->
<xsl:template match="ID" mode="M0" priority="1000">
    <svrl:fired-rule context="ID" />

        <!--ASSERT -->
<xsl:choose>
      <xsl:when test="string-length(.) > 9" />
      <xsl:otherwise>
        <svrl:failed-assert test="string-length(.) > 9">
          <xsl:attribute name="location">
            <xsl:apply-templates mode="schematron-select-full-path" select="." />
          </xsl:attribute>
          <svrl:text>Something with chars.</svrl:text>
        </svrl:failed-assert>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:apply-templates mode="M0" select="*|comment()|processing-instruction()" />
  </xsl:template>
  <xsl:template match="text()" mode="M0" priority="-1" />
  <xsl:template match="@*|node()" mode="M0" priority="-2">
    <xsl:apply-templates mode="M0" select="*|comment()|processing-instruction()" />
  </xsl:template>
</xsl:stylesheet>

XML:

<?xml version="1.0" encoding="UTF-8"?>
<test>Test</test>

Error: 08:19:23.865 [main] ERROR com.helger.xml.transform.LoggingTransformErrorListener -- [fatal_error] Transformation fatal error (net.sf.saxon.trans.XPathException: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: Es wurde versucht, einen Knoten an einer Stelle einzufügen, an der dies nicht zulässig ist.) 08:19:23.869 [main] ERROR com.helger.schematron.schxslt.xslt2.SchematronProviderXSLTFromSchXslt_XSLT2 -- SchXslt preprocessor error net.sf.saxon.trans.XPathException: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: Es wurde versucht, einen Knoten an einer Stelle einzufügen, an der dies nicht zulässig ist. 08:19:23.876 [main] WARN com.helger.schematron.schxslt.xslt2.SchematronResourceSchXslt_XSLT2Cache -- The Schematron resource 'file:/C:/.../arche.xslt' is invalid! 08:19:23.876 [main] WARN com.helger.schematron.api.xslt.AbstractSchematronXSLTBasedResource -- Cannot apply the Schematron validation, due to errors in the Schematron rules

phax commented 3 months ago

Sorry, it took me some time to figure this one out. As you previously converted the SCH to XSLT, then you must use SchematronResourceXSLT (from the ph-schematron-xslt module). SchXslt can only be used when you do "SCH -> XSLT -> SVRL" in one go. The Maven plugin always uses the "ISO Schematron" conversion. Even if you convert with SchXslt from SCH to XSLT, you need "ph-schematron-xslt" to apply the XSLT.

Hope that makes sense

oliverunger commented 3 months ago

Im still confused by this. I wanted to use Schxslt because Iso Schematron requires some license, right? But I want to precompile my Schematron files using the maven plugin at build time for better performance at runtime. But theres no way to go to have both right?

phax commented 3 months ago

No it doesn't require a fee. You are mixing things up:

So no fee included in any of this.

oliverunger commented 3 months ago

Ok then I go with ISO schematron to be able to use the plugin. Thank you for your investigation, quick responses and explanation!