phax / ph-schematron

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

Inconsistent Error Handling: XSLT-Based Resource Fails, While Pure Resource Completes Validation #171

Closed gediminasre closed 3 months ago

gediminasre commented 4 months ago

Dear Repository Maintainer,

We are encountering an issue where there is a discrepancy between Pure and XSLT validations using the same Schematron file in version 7.1.2 of your library.

Description:

During our validation process, Pure validation logs an error message to the console but continues and completes the validation. In contrast, XSLT validation, using the same Schematron file, throws the same error and halts the validation process.

Error Messages and Files:

Issue:

For some reason, the Pure validation catches the error but does not re-throw it, whereas the XSLT validation re-throws the error, causing it to stop. This difference in behavior is causing inconsistencies in our validation workflow.

Could you please investigate why the XSLT validation behaves differently from the Pure validation in this scenario? Any insights or suggestions for achieving consistent behavior would be greatly appreciated.

Thank you for your assistance and for providing such a valuable tool.

Best regards, Gediminas

gediminasre commented 3 months ago

@phax perhaps you had some time to look at this?

phax commented 3 months ago

@gediminasre thanks for the reminder. Indeed there is an explanation for this. a) Pure and XSLT behave totally different because they support a different condition language (XPath vs. XPath + XSLT) and the execution logic is different (Java vs. XSLT element iteration) b) See e.g. #169 for another very similar case on why they behave differently

However, I acknowledge the inconsistency, that the error is not re-thrown and I will look into it

gediminasre commented 3 months ago

Thanks for coming back @phax

To clarify, XSLT should ideally follow the Pure validation pattern and not throw the error, allowing the full validation process to complete.

phax commented 3 months ago

@gediminasre does an SVRL like this suit your needs:

<?xml version="1.0" encoding="UTF-8"?>
<svrl:schematron-output xmlns:svrl="http://purl.oclc.org/dsdl/svrl" title="eForms-DE Schematron Version @eforms-de-schematron.version.full@ compliant with eForms-DE specification @eforms-de.version.full@" phase="doe-validation-phase">
  <svrl:ns-prefix-in-attribute-values prefix="can" uri="urn:oasis:names:specification:ubl:schema:xsd:ContractAwardNotice-2" />
  <svrl:ns-prefix-in-attribute-values prefix="cn" uri="urn:oasis:names:specification:ubl:schema:xsd:ContractNotice-2" />
  <svrl:ns-prefix-in-attribute-values prefix="pin" uri="urn:oasis:names:specification:ubl:schema:xsd:PriorInformationNotice-2" />
  <svrl:ns-prefix-in-attribute-values prefix="brin" uri="http://data.europa.eu/p27/eforms-business-registration-information-notice/1" />
  <svrl:ns-prefix-in-attribute-values prefix="cbc" uri="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" />
  <svrl:ns-prefix-in-attribute-values prefix="cac" uri="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" />
  <svrl:ns-prefix-in-attribute-values prefix="ext" uri="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" />
  <svrl:ns-prefix-in-attribute-values prefix="efac" uri="http://data.europa.eu/p27/eforms-ubl-extension-aggregate-components/1" />
  <svrl:ns-prefix-in-attribute-values prefix="efext" uri="http://data.europa.eu/p27/eforms-ubl-extensions/1" />
  <svrl:ns-prefix-in-attribute-values prefix="efbc" uri="http://data.europa.eu/p27/eforms-ubl-extension-basic-components/1" />
  <svrl:ns-prefix-in-attribute-values prefix="xs" uri="http://www.w3.org/2001/XMLSchema" />
  <svrl:active-pattern id="global-variable-pattern" />
  <svrl:active-pattern id="technical-sanity-pattern" />
  <svrl:fired-rule context="//$ROOT-NODE/cbc:CustomizationID" />
  <svrl:failed-assert id="SR-DE-1" location="/can:ContractAwardNotice/cbc:CustomizationID" test="text() = $EFORMS-DE-ID" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'text() = $EFORMS-DE-ID'
Error: The string "eforms-de-1.1" cannot be cast to a boolean</svrl:text>
  </svrl:failed-assert>
  <svrl:active-pattern id="cardinality-pattern" />
  <svrl:fired-rule context="//$ROOT-NODE" />
  <svrl:failed-assert id="CR-DE-BT-105" location="/can:ContractAwardNotice" test="if ($SUBTYPE = $SUBTYPES-BT-105) then             exists(cac:TenderingProcess/cbc:ProcedureCode)           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-105) then             exists(cac:TenderingProcess/cbc:ProcedureCode)           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:fired-rule context="//$ROOT-NODE/cbc:RequestedPublicationDate" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProject" />
  <svrl:failed-assert id="CR-DE-BT-21" location="/can:ContractAwardNotice/cac:ProcurementProject" test="boolean(normalize-space((cbc:Name[./@languageID = $MAIN-LANG])))" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'boolean(normalize-space((cbc:Name[./@languageID = $MAIN-LANG])))'
Error: The string "DEU" cannot be cast to a boolean</svrl:text>
  </svrl:failed-assert>
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProject/cac:RealizedLocation" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProjectLot[cbc:ID/@schemeName = 'Lot']/cac:ProcurementProject" />
  <svrl:failed-assert id="CR-DE-BT-21-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:ProcurementProject" test="boolean(normalize-space((cbc:Name[./@languageID = $MAIN-LANG])))" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'boolean(normalize-space((cbc:Name[./@languageID = $MAIN-LANG])))'
Error: The string "DEU" cannot be cast to a boolean</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="BR-DE-21-A" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:ProcurementProject" test="if ($SUBTYPE = $SUBTYPES-BT-06) then             (count(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement']) ge 1)           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-06) then             (count(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement']) ge 1)           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="BR-DE-21-B" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:ProcurementProject" test="if ($SUBTYPE = $SUBTYPES-BT-06) then             (count(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement']) le 3)           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-06) then             (count(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement']) le 3)           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="BR-DE-22" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:ProcurementProject" test="if ($SUBTYPE = $SUBTYPES-BT-06) then             (count(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement']) = count(distinct-values(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement'])))           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-06) then             (count(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement']) = count(distinct-values(cac:ProcurementAdditionalType/cbc:ProcurementTypeCode[@listName = 'strategic-procurement'])))           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProjectLot[cbc:ID/@schemeName = 'Lot' or cbc:ID/@schemeName = 'Part']/cac:ProcurementProject/cac:RealizedLocation/cac:Address" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProjectLot[cbc:ID/@schemeName = 'Lot']/cac:TenderingTerms" />
  <svrl:failed-assert id="CR-DE-BT-771-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:TenderingTerms" test="if ($SUBTYPE = $SUBTYPES-BT-771-772) then             (count(cac:TendererQualificationRequest[not(cbc:CompanyLegalFormCode)]/cac:SpecificTendererRequirement[(cbc:TendererRequirementTypeCode[@listName = 'missing-info-submission'])]) = 1)           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-771-772) then             (count(cac:TendererQualificationRequest[not(cbc:CompanyLegalFormCode)]/cac:SpecificTendererRequirement[(cbc:TendererRequirementTypeCode[@listName = 'missing-info-submission'])]) = 1)           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="CR-DE-BT-772-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:TenderingTerms" test="if ($SUBTYPE = $SUBTYPES-BT-771-772) then             (count(cac:TendererQualificationRequest[not(cbc:CompanyLegalFormCode)]/cac:SpecificTendererRequirement[(cbc:TendererRequirementTypeCode[@listName = 'missing-info-submission'])]/cbc:Description[./@languageID = $MAIN-LANG]) = 1)                      else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-771-772) then             (count(cac:TendererQualificationRequest[not(cbc:CompanyLegalFormCode)]/cac:SpecificTendererRequirement[(cbc:TendererRequirementTypeCode[@listName = 'missing-info-submission'])]/cbc:Description[./@languageID = $MAIN-LANG]) = 1)                      else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="SR-DE-21" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:TenderingTerms" test="if ($SUBTYPE = $SUBTYPES-BT-771-772) then             (count(cac:TendererQualificationRequest) >= 1)           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-771-772) then             (count(cac:TendererQualificationRequest) &gt;= 1)           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="CR-DE-BT-97-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:TenderingTerms" test="if ($SUBTYPE = $SUBTYPES-BT-97) then             exists(cac:Language/cbc:ID)           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-97) then             exists(cac:Language/cbc:ID)           else             true()'
Error: Cannot compare xs:boolean to xs:string</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="SR-DE-23" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:TenderingTerms" test="if ($SUBTYPE = $SUBTYPES-BT-15) then             (count(cac:CallForTendersDocumentReference) >= 1)           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-15) then             (count(cac:CallForTendersDocumentReference) &gt;= 1)           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProjectLot[cbc:ID/@schemeName = 'Lot']" />
  <svrl:failed-assert id="CR-DE-BT-63-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot" test="if ($SUBTYPE = $SUBTYPES-BT-63) then             boolean(normalize-space(cac:TenderingTerms/cbc:VariantConstraintCode))           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-63) then             boolean(normalize-space(cac:TenderingTerms/cbc:VariantConstraintCode))           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="CR-DE-BT-17-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot" test="if ($SUBTYPE = $SUBTYPES-BT-17) then             boolean(normalize-space(cac:TenderingProcess/cbc:SubmissionMethodCode[@listName = 'esubmission']))           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-17) then             boolean(normalize-space(cac:TenderingProcess/cbc:SubmissionMethodCode[@listName = 'esubmission']))           else             true()'
Error: Cannot compare xs:boolean to xs:string</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="CR-DE-BT-717-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot" test="if ($SUBTYPE = $SUBTYPES-BT-717) then             exists(cac:TenderingTerms/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/efext:EformsExtension/efac:StrategicProcurement/efbc:ApplicableLegalBasis[@listName = 'cvd-scope'])           else             true()">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-717) then             exists(cac:TenderingTerms/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/efext:EformsExtension/efac:StrategicProcurement/efbc:ApplicableLegalBasis[@listName = 'cvd-scope'])           else             true()'
Error: Cannot compare xs:boolean to xs:double</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert id="CR-DE-BT-769-Lot" location="/can:ContractAwardNotice/cac:ProcurementProjectLot" test="if ($SUBTYPE = $SUBTYPES-BT-769) then             boolean(normalize-space(cac:TenderingTerms/cbc:MultipleTendersCode))           else             true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if ($SUBTYPE = $SUBTYPES-BT-769) then             boolean(normalize-space(cac:TenderingTerms/cbc:MultipleTendersCode))           else             true()'
Error: Cannot compare xs:boolean to xs:string</svrl:text>
  </svrl:failed-assert>
  <svrl:active-pattern id="codelists" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:ContractingParty/cac:ContractingPartyType/cbc:PartyTypeCode[@listName = 'buyer-legal-type']" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:TenderingProcess/cbc:ProcedureCode" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:TenderingTerms/cac:ProcurementLegislationDocumentReference/cbc:ID" />
  <svrl:active-pattern id="conditional-mandatory" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProjectLot[cbc:ID/@schemeName = 'Lot']/cac:ProcurementProject/cac:ProcurementAdditionalType" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProjectLot[cbc:ID/@schemeName = ('Part', 'Lot', 'LotsGroup')]/cac:ProcurementProject/cbc:Note" />
  <svrl:fired-rule context="//$ROOT-NODE/cac:ProcurementProjectLot[cbc:ID/@schemeName = ('Lot', 'Part')]/cac:TenderingTerms/cac:AwardingTerms/cac:AwardingCriterion/cac:SubordinateAwardingCriterion" />
  <svrl:failed-assert id="BR-DE-23" location="/can:ContractAwardNotice/cac:ProcurementProjectLot/cac:TenderingTerms/cac:AwardingTerms/cac:AwardingCriterion/cac:SubordinateAwardingCriterion" test="if         (normalize-space($AwardCriterionParameter/efbc:ParameterCode/text()) = 'per-exa' and         number(normalize-space($AwardCriterionParameter/efbc:ParameterNumeric/text())) ge 10         )         then           (boolean(normalize-space(cbc:AwardingCriterionTypeCode)) and           boolean(normalize-space(cbc:Name[./@languageID = $MAIN-LANG]))           )         else           true()" role="error">
    <svrl:text>Failed to evaluate XPath expression to a boolean.
Test: 'if         (normalize-space($AwardCriterionParameter/efbc:ParameterCode/text()) = 'per-exa' and         number(normalize-space($AwardCriterionParameter/efbc:ParameterNumeric/text())) ge 10         )         then           (boolean(normalize-space(cbc:AwardingCriterionTypeCode)) and           boolean(normalize-space(cbc:Name[./@languageID = $MAIN-LANG]))           )         else           true()'
Error: A sequence of more than one item is not allowed as the first argument of fn:normalize-space() ("0.4", "0.6") </svrl:text>
  </svrl:failed-assert>
  <svrl:active-pattern id="doe-validation-pattern" />
  <svrl:fired-rule context="//$ROOT-NODE" />
  <svrl:failed-assert location="/can:ContractAwardNotice" test="$BT-05-DATE ge $CURRENT-DATE - xs:dayTimeDuration('P1D')">
    <svrl:text>Date of BT-05= must be less than 1 day in the past. Current date=2024-07-12+02:00. Difference=</svrl:text>
  </svrl:failed-assert>
  <svrl:failed-assert location="/can:ContractAwardNotice" test="$BT-05-DATE le $CURRENT-DATE + xs:dayTimeDuration('P1D')">
    <svrl:text>Date of BT-05= must be less than 1 day in the future. Current date=2024-07-12+02:00. Difference=$CURRENT-DATE + xs:dayTimeDuration('P1D')</svrl:text>
  </svrl:failed-assert>
</svrl:schematron-output>
gediminasre commented 3 months ago

@phax this SVRL report looks just what we need. Thank you very much!

phax commented 3 months ago

Great :) Will be part of the next 8.x release