Skip to content
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 European Union
* Copyright 2025 European Union
*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European
* Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in
Expand All @@ -13,21 +13,121 @@
*/
package eu.europa.ted.eforms.sdk.schematron;

import java.util.List;

import eu.europa.ted.efx.model.Context;
import eu.europa.ted.efx.model.rules.RuleNature;
import eu.europa.ted.efx.model.rules.RuleSeverity;
import eu.europa.ted.efx.model.rules.ValidationRule;
import eu.europa.ted.efx.model.variables.DynamicVariable;

/**
* Represents a Schematron <assert> element.
* Fires when the test expression evaluates to false.
* For rules referencing dynamic variables, the test is guarded so that API errors
* do not cause the main rule to fire — a companion {@link NoApiError} assert handles that.
*/
public class SchematronAssert extends SchematronTest {

public SchematronAssert(ValidationRule rule, Context ruleContext) {
private final List<DynamicVariable> dynamicVariables;

public SchematronAssert(final ValidationRule rule, final Context ruleContext) {
super(rule, ruleContext);
this.dynamicVariables = rule.findReferencedDynamicVariables();
}

@Override
public RuleNature getRuleNature() {
if (!this.dynamicVariables.isEmpty()) {
return RuleNature.DYNAMIC;
}
return super.getRuleNature();
}

@Override
public String getElementName() {
return "assert";
}

@Override
public String getTest() {
String baseTest = super.getTest();
if (this.getRuleNature() != RuleNature.DYNAMIC) {
return baseTest;
}
// An assert fires when the test is false. Prepending "($varName = -1) or" makes the
// test true when any dynamic variable errored, preventing the main assert from firing.
// Companion NoApiError asserts handle API errors separately.
StringBuilder sb = new StringBuilder();
for (var dynamicVar : this.dynamicVariables) {
sb.append("($").append(dynamicVar.name).append(" = -1) or ");
}
for (var variable : this.rule.getAutoGeneratedVariables()) {
sb.append("($").append(variable.name).append(" = -1) or ");
}
if (this.rule.getCondition() != null) {
// WHEN clause: combineWithOrParenthesized already wrapped each operand in parens.
sb.append(baseTest);
} else {
// No WHEN clause: the raw expression needs wrapping to isolate it from the guards.
sb.append("(").append(baseTest).append(")");
}
return sb.toString();
}

/**
* A companion Schematron &lt;assert&gt; that checks a dynamic variable did not return an error (-1).
* Generated for each dynamic variable (declared or auto-generated) referenced by a rule.
*/
public static class NoApiError extends SchematronAssert {

private final DynamicVariable dynamicVariable;

public NoApiError(ValidationRule rule, DynamicVariable dynamicVariable, Context ruleContext) {
super(rule, ruleContext);
this.dynamicVariable = dynamicVariable;
}

@Override
public RuleNature getRuleNature() {
return RuleNature.DYNAMIC;
}

@Override
public SchematronLet getLetElement() {
if (this.dynamicVariable instanceof DynamicVariable.AutoGenerated) {
return new SchematronLet(this.dynamicVariable);
}
return null;
}

@Override
public String getId() {
return this.rule.getId() + "-api-error-" + this.dynamicVariable.identity();
}

@Override
public String getRole() {
return this.dynamicVariable.errorSeverity().toString().toUpperCase();
}

@Override
public String getTest() {
return "not($" + this.dynamicVariable.name + " = -1)";
}

@Override
public String getMessage() {
if (this.dynamicVariable.errorLabel() != null) {
return this.dynamicVariable.errorLabel();
}
return this.dynamicVariable.errorSeverity() == RuleSeverity.WARNING
? "rule|text|api-warning" : "rule|text|api-error";
}

@Override
public SchematronDiagnostic getDiagnostic() {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 European Union
* Copyright 2025 European Union
*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European
* Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in
Expand Down
Loading
Loading