diff --git a/pom.xml b/pom.xml index b0234842..5d555db4 100644 --- a/pom.xml +++ b/pom.xml @@ -48,11 +48,12 @@ 1.13.0 2.0.0-SNAPSHOT - 1.5.0 + 1.6.0-SNAPSHOT 4.13.1 3.18.0 + 2.3.31 1.5.18 2.18.3 2.18.3 @@ -121,6 +122,11 @@ antlr4-runtime ${version.antlr4} + + org.freemarker + freemarker + ${version.freemarker} + @@ -146,6 +152,10 @@ antlr4-runtime compile + + org.freemarker + freemarker + com.fasterxml.jackson.core jackson-core diff --git a/src/main/java/eu/europa/ted/eforms/sdk/ComponentFactory.java b/src/main/java/eu/europa/ted/eforms/sdk/ComponentFactory.java index 41671d0d..33db51b9 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/ComponentFactory.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/ComponentFactory.java @@ -11,6 +11,7 @@ import eu.europa.ted.efx.interfaces.ScriptGenerator; import eu.europa.ted.efx.interfaces.SymbolResolver; import eu.europa.ted.efx.interfaces.TranslatorOptions; +import eu.europa.ted.efx.interfaces.ValidatorGenerator; public class ComponentFactory extends SdkComponentFactory { public static final ComponentFactory INSTANCE = new ComponentFactory(); @@ -96,7 +97,7 @@ public static synchronized SymbolResolver getSymbolResolver(final String sdkVers // Create new instance (this can throw InstantiationException) SymbolResolver newInstance = ComponentFactory.INSTANCE.getComponentImpl(sdkVersion, SdkComponentType.SYMBOL_RESOLVER, qualifier, SymbolResolver.class, sdkVersion, sdkRootPath); - + // Store and return instances.put(key, newInstance); return newInstance; @@ -113,6 +114,18 @@ public static MarkupGenerator getMarkupGenerator(final String sdkVersion, final SdkComponentType.MARKUP_GENERATOR, qualifier, MarkupGenerator.class, options); } + public static ValidatorGenerator getValidatorGenerator(final String sdkVersion, + TranslatorOptions options) throws InstantiationException { + return getValidatorGenerator(sdkVersion, "", options); + } + + public static ValidatorGenerator getValidatorGenerator(final String sdkVersion, + final String qualifier, TranslatorOptions options) throws InstantiationException { + return ComponentFactory.INSTANCE.getComponentImpl(sdkVersion, + SdkComponentType.VALIDATOR_GENERATOR, qualifier, ValidatorGenerator.class, + options); + } + public static ScriptGenerator getScriptGenerator(final String sdkVersion, TranslatorOptions options) throws InstantiationException { return getScriptGenerator(sdkVersion, "", options); diff --git a/src/main/java/eu/europa/ted/eforms/sdk/SdkSymbolResolver.java b/src/main/java/eu/europa/ted/eforms/sdk/SdkSymbolResolver.java index b8317013..2c403dbd 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/SdkSymbolResolver.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/SdkSymbolResolver.java @@ -14,9 +14,11 @@ import eu.europa.ted.eforms.sdk.entity.SdkCodelist; import eu.europa.ted.eforms.sdk.entity.SdkField; import eu.europa.ted.eforms.sdk.entity.SdkNode; +import eu.europa.ted.eforms.sdk.entity.SdkNoticeSubtype; import eu.europa.ted.eforms.sdk.repository.SdkCodelistRepository; import eu.europa.ted.eforms.sdk.repository.SdkFieldRepository; import eu.europa.ted.eforms.sdk.repository.SdkNodeRepository; +import eu.europa.ted.eforms.sdk.repository.SdkNoticeTypeRepository; import eu.europa.ted.eforms.sdk.resource.SdkResourceLoader; import eu.europa.ted.eforms.xpath.XPathInfo; import eu.europa.ted.eforms.xpath.XPathProcessor; @@ -41,6 +43,8 @@ public class SdkSymbolResolver implements SymbolResolver { protected Map codelistById; + protected Map noticeTypesById; + /** * Builds EFX list from the passed codelist reference. This will lazily compute * and cache the @@ -77,12 +81,15 @@ protected void loadMapData(final String sdkVersion, final Path sdkRootPath) SdkConstants.SdkResource.FIELDS_JSON, sdkRootPath); Path codelistsPath = SdkResourceLoader.getResourceAsPath(sdkVersion, SdkConstants.SdkResource.CODELISTS, sdkRootPath); + Path noticeTypesPath = SdkResourceLoader.getResourceAsPath(sdkVersion, + SdkConstants.SdkResource.NOTICE_TYPES_JSON, sdkRootPath); this.fieldById = new SdkFieldRepository(sdkVersion, jsonPath); this.fieldByAlias = indexFieldsByAlias(); this.nodeById = new SdkNodeRepository(sdkVersion, jsonPath); this.nodeByAlias = indexNodesByAlias(); this.codelistById = new SdkCodelistRepository(sdkVersion, codelistsPath); + this.noticeTypesById = new SdkNoticeTypeRepository(sdkVersion, noticeTypesPath); } /** @@ -227,6 +234,10 @@ public String getNodeIdFromAlias(String alias) { return null; } + @Override + public List getAllNoticeSubtypeIds() { + return noticeTypesById.keySet().stream().map(String::toUpperCase).sorted().toList(); + } private HashMap indexFieldsByAlias() { return this.fieldById.values().stream() @@ -268,8 +279,8 @@ private void cacheAdditionalFieldInfo(final String fieldId) { } XPathInfo xpathInfo = XPathProcessor.parse(this.getAbsolutePathOfField(fieldId).getScript()); additionalFieldInfoMap.put(fieldId, xpathInfo); - } - + } + // #endregion Temporary helpers ------------------------------------------------ } diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronAssert.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronAssert.java new file mode 100644 index 00000000..1a89197f --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronAssert.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import eu.europa.ted.efx.model.Context; +import eu.europa.ted.efx.model.rules.ValidationRule; + +/** + * Represents a Schematron <assert> element. + * Fires when the test expression evaluates to false. + */ +public class SchematronAssert extends SchematronTest { + + public SchematronAssert(ValidationRule rule, Context ruleContext) { + super(rule, ruleContext); + } + + @Override + public String getElementName() { + return "assert"; + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronDiagnostic.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronDiagnostic.java new file mode 100644 index 00000000..749d0e6a --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronDiagnostic.java @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import eu.europa.ted.eforms.xpath.XPathProcessor; +import eu.europa.ted.efx.model.Context; + +/** + * Represents a Schematron <diagnostic> element. + * Used in the <diagnostics> section of complete-validation.sch. + * Format: <diagnostic id="..." see="field:...">xpath</diagnostic> + */ +public class SchematronDiagnostic { + + private final String id; + private final String seeAttribute; + private final String xpath; + + private static String sanitize(String identifier) { + return identifier.replace("(", "_").replace(")", "_"); + } + + public SchematronDiagnostic(Context subject, Context ruleContext) { + this.xpath = XPathProcessor.contextualize( + ruleContext.absolutePath().getScript(), subject.absolutePath().getScript()); + this.id = sanitize(ruleContext.symbol()) + "_" + sanitize(subject.symbol()); + String prefix = subject.isFieldContext() ? "field:" : "node:"; + this.seeAttribute = prefix + subject.symbol(); + } + + /** Used by pattern.ftl and complete-validation.ftl */ + public String getId() { + return this.id; + } + + /** Used by complete-validation.ftl */ + public String getSeeAttribute() { + return this.seeAttribute; + } + + /** Used by complete-validation.ftl */ + public String getXpath() { + return this.xpath; + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronGenerator.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronGenerator.java new file mode 100644 index 00000000..33108d3e --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronGenerator.java @@ -0,0 +1,363 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import eu.europa.ted.eforms.sdk.component.SdkComponent; +import eu.europa.ted.eforms.sdk.component.SdkComponentType; +import eu.europa.ted.efx.interfaces.ValidatorGenerator; +import eu.europa.ted.efx.model.rules.CompleteValidation; +import eu.europa.ted.efx.model.rules.ValidationStage; +import eu.europa.ted.efx.model.variables.Variable; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; + +/** + * Generates Schematron XML markup using Freemarker templates. + * + * This class is responsible for transforming the intermediate validation model + * (ValidationStage) into Schematron XML format. It implements ValidatorGenerator + * to provide a clean separation between translation and output generation. + */ +@SdkComponent(versions = {"2"}, componentType = SdkComponentType.VALIDATOR_GENERATOR) +public class SchematronGenerator implements ValidatorGenerator { + + private static final Logger logger = LoggerFactory.getLogger(SchematronGenerator.class); + + private static final ObjectMapper JSON_MAPPER = new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT); + + private final Configuration freemarkerConfig; + + public SchematronGenerator() { + this.freemarkerConfig = new Configuration(Configuration.VERSION_2_3_31); + this.freemarkerConfig.setClassForTemplateLoading(SchematronGenerator.class, + "/freemarker/schematron"); + this.freemarkerConfig.setDefaultEncoding("UTF-8"); + this.freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + this.freemarkerConfig.setLogTemplateExceptions(false); + } + + // #region ValidatorMarkupGenerator Implementation + + @Override + public Map generateOutput( + CompleteValidation completeValidation + ) throws IOException { + logger.debug("Generating Schematron output from {} stages", completeValidation.getStages().size()); + + // Create local state for this generation run + SchematronSchema schema = new SchematronSchema("eForms schematron rules"); + List patterns = new ArrayList<>(); + Map diagnosticsMap = new LinkedHashMap<>(); + + // Add global variables to schema + for (Variable variable : completeValidation.getGlobalVariables()) { + String xpathValue = variable.initializationExpression.getScript(); + SchematronLet globalVar = new SchematronLet(variable.name, xpathValue); + schema.addGlobalVariable(globalVar); + logger.debug("Added global variable: {} = {}", variable.name, xpathValue); + } + + // Transform intermediate model (ValidationStage) to Schematron model (SchematronPattern) + transformStagesToPatterns(completeValidation.getStages(), patterns, diagnosticsMap); + + // Add collected diagnostics to schema + addDiagnosticsToSchema(diagnosticsMap, schema); + + // Generate all output files + return generateOutputFiles(completeValidation.getNoticeSubtypes(), patterns, schema); + } + + // #endregion ValidatorMarkupGenerator Implementation + + // #region Freemarker Template Methods + + /** + * Generates the complete-validation.sch file content. + * + * @param schema The SchematronSchema object containing global variables, phases, and includes. + * @return The XML content of complete-validation.sch as a string. + * @throws IOException If template loading fails. + * @throws TemplateException If template processing fails. + */ + public String generateCompleteValidation(SchematronSchema schema) + throws IOException, TemplateException { + Template template = freemarkerConfig.getTemplate("complete-validation.ftl"); + StringWriter writer = new StringWriter(); + template.process(schema, writer); + return writer.toString(); + } + + /** + * Generates a single pattern file content (e.g., validation-stage-1a.sch). + * + * @param pattern The SchematronPattern object containing rules and assertions. + * @param config The output configuration specifying which rule natures to include. + * @return The XML content of the pattern file as a string. + * @throws IOException If template loading fails. + * @throws TemplateException If template processing fails. + */ + public String generatePattern(SchematronPattern pattern, SchematronOutputConfig config) + throws IOException, TemplateException { + Template template = freemarkerConfig.getTemplate("pattern.ftl"); + StringWriter writer = new StringWriter(); + + Map model = new HashMap<>(); + model.put("id", pattern.getId()); + model.put("variables", pattern.getVariables()); + model.put("rules", pattern.getRules()); + model.put("tags", config.ruleNatures().stream() + .map(Enum::name) + .toList()); + + template.process(model, writer); + return writer.toString(); + } + + // #endregion Freemarker Template Methods + + // #region Transformation Methods + + /** + * Transforms validation stages into Schematron patterns. + * Creates one pattern per (stage, noticeType) combination, where each pattern + * only contains the rules/assertions that apply to that specific notice type. + */ + private void transformStagesToPatterns(List stages, + List patterns, Map diagnosticsMap) { + for (ValidationStage stage : stages) { + // Get all notice types referenced in this stage + for (String noticeType : SchematronPattern.getNoticeTypesInStage(stage)) { + SchematronPattern pattern = new SchematronPattern(stage, noticeType); + if (pattern.hasRules()) { + patterns.add(pattern); + diagnosticsMap.putAll(pattern.getDiagnostics()); + logger.debug("Created pattern {} for stage {} / notice type {}", + pattern.getId(), stage.getName(), noticeType); + } + } + } + } + + private void addDiagnosticsToSchema(Map diagnosticsMap, + SchematronSchema schema) { + for (SchematronDiagnostic diagnostic : diagnosticsMap.values()) { + schema.addDiagnostic(diagnostic); + } + } + + // #endregion Transformation Methods + + // #region Output Generation Methods + + /** + * Generates all Schematron output files from the processed patterns and schema. + * This includes individual pattern files, the complete-validation.sch master file, + * and the schematrons.json metadata file. + * + * Output is organized into subfolders based on rule nature: + * - dynamic/: Contains all rules (static + dynamic) for full validation + * - static/: Contains only static rules for validation without external services + * + * @return A map of filename to file content for all generated Schematron files + * @throws IOException If an error occurs during file generation + */ + private Map generateOutputFiles(List noticeTypeIds, + List patterns, SchematronSchema baseSchema) throws IOException { + logger.debug("Generating Schematron output files"); + + Map outputFiles = new HashMap<>(); + List> schematronsMetadata = new ArrayList<>(); + + // Generate output for each configuration (DYNAMIC first, then STATIC) + List configs = List.of( + SchematronOutputConfig.DYNAMIC, + SchematronOutputConfig.STATIC + ); + + try { + for (SchematronOutputConfig config : configs) { + generateOutputForConfig(config, noticeTypeIds, patterns, baseSchema, outputFiles, schematronsMetadata); + } + + // Generate schematrons.json with entries from all configurations + String schematronsJson = generateSchematronsJson(schematronsMetadata); + outputFiles.put("schematrons.json", schematronsJson); + + logger.debug("Generated {} Schematron files", outputFiles.size()); + + return outputFiles; + + } catch (TemplateException e) { + throw new IOException("Failed to generate Schematron XML", e); + } + } + + /** + * Generates output files for a specific configuration (e.g., DYNAMIC or STATIC). + */ + private void generateOutputForConfig( + SchematronOutputConfig config, + List noticeTypeIds, + List patterns, + SchematronSchema baseSchema, + Map outputFiles, + List> schematronsMetadata) throws IOException, TemplateException { + + String folderPrefix = config.folderName() + "/"; + String configType = config.folderName(); + + // Create a fresh schema for this configuration + SchematronSchema schema = new SchematronSchema(baseSchema.getTitle()); + for (SchematronLet globalVar : baseSchema.getGlobalVariables()) { + schema.addGlobalVariable(globalVar); + } + for (SchematronDiagnostic diagnostic : baseSchema.getDiagnostics()) { + schema.addDiagnostic(diagnostic); + } + + List generatedPatternIds = new ArrayList<>(); + + // Generate individual pattern files for this configuration + for (SchematronPattern pattern : patterns) { + // Skip patterns that have no rules matching this configuration + if (!pattern.hasRulesFor(config.ruleNatures())) { + logger.debug("Skipping pattern {} for {} (no matching rules)", pattern.getId(), configType); + continue; + } + + String patternXml = generatePattern(pattern, config); + String patternId = pattern.getId(); + String filename = folderPrefix + patternId + ".sch"; + outputFiles.put(filename, patternXml); + + // Add include to schema (relative path within the folder) + schema.addInclude(patternId + ".sch"); + generatedPatternIds.add(patternId); + + // Add to metadata + Map metadata = new LinkedHashMap<>(); + metadata.put("name", patternId); + metadata.put("type", configType); + if (pattern.getStage() != null) { + metadata.put("stage", pattern.getStage()); + } + metadata.put("filename", filename); + schematronsMetadata.add(metadata); + + logger.debug("Generated {} pattern file: {}", configType, filename); + } + + // Build and add phases to schema + Map> phasesMap = buildPhasesMapForConfig(noticeTypeIds, patterns, config); + for (Map.Entry> entry : phasesMap.entrySet()) { + String noticeTypeId = entry.getKey(); + List patternIds = entry.getValue(); + + if (!patternIds.isEmpty()) { + SchematronPhase phase = new SchematronPhase(noticeTypeId); + for (String patternId : patternIds) { + phase.addActivePattern(patternId); + } + schema.addPhase(phase); + logger.debug("Added phase {} with {} patterns for {}", phase.getId(), patternIds.size(), configType); + } + } + + // Generate complete-validation.sch for this configuration + String completeValidation = generateCompleteValidation(schema); + String completeFilename = folderPrefix + "complete-validation.sch"; + outputFiles.put(completeFilename, completeValidation); + + // Add complete-validation to metadata at the beginning of this config's entries + Map completeMetadata = new LinkedHashMap<>(); + completeMetadata.put("name", "complete-validation"); + completeMetadata.put("type", configType); + completeMetadata.put("filename", completeFilename); + + // Find the insertion point (before the pattern entries for this config) + int insertIndex = schematronsMetadata.size() - generatedPatternIds.size(); + schematronsMetadata.add(insertIndex, completeMetadata); + + logger.debug("Generated {} complete-validation.sch", configType); + } + + /** + * Builds a map from notice type ID to list of pattern IDs that apply to it, + * filtered by the given output configuration. + * Each pattern now applies to exactly one notice type, so this is a simple grouping. + * Pattern order is preserved from the EFX file order. + * + * @param config The output configuration specifying which rule natures to include + * @return Map of notice type ID to ordered list of pattern IDs + */ + private Map> buildPhasesMapForConfig(List noticeTypeIds, + List patterns, SchematronOutputConfig config) { + Map> phasesMap = new LinkedHashMap<>(); + + // Initialize map with all valid notice types + for (String noticeTypeId : noticeTypeIds) { + phasesMap.put(noticeTypeId, new ArrayList<>()); + } + + // Each pattern applies to exactly one notice type + for (SchematronPattern pattern : patterns) { + // Skip patterns that don't have rules matching this configuration + if (!pattern.hasRulesFor(config.ruleNatures())) { + continue; + } + String noticeType = pattern.getNoticeType(); + List patternList = phasesMap.get(noticeType); + if (patternList != null) { + patternList.add(pattern.getId()); + } + } + + return phasesMap; + } + + // #endregion Output Generation Methods + + // #region Helper Methods + + private String generateSchematronsJson(List> schematronsMetadata) { + Map root = new LinkedHashMap<>(); + root.put("schematrons", schematronsMetadata); + + try { + return JSON_MAPPER.writeValueAsString(root) + "\n"; + } catch (JsonProcessingException e) { + throw new RuntimeException("Failed to serialize schematrons.json", e); + } + } + + // #endregion Helper Methods +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronLet.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronLet.java new file mode 100644 index 00000000..ac426b27 --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronLet.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +/** + * Represents a Schematron <let> element for variable declarations. + */ +public class SchematronLet { + private final String name; + private final String value; + + public SchematronLet(String name, String value) { + this.name = name; + this.value = value; + } + + /** Used by pattern.ftl and complete-validation.ftl */ + public String getName() { + return this.name; + } + + /** Used by pattern.ftl and complete-validation.ftl */ + public String getValue() { + return this.value; + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronOutputConfig.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronOutputConfig.java new file mode 100644 index 00000000..d868ddab --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronOutputConfig.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import java.util.EnumSet; + +import eu.europa.ted.efx.model.rules.RuleNature; + +/** + * Configuration for Schematron output generation. + * Pairs a folder name with the set of rule natures to include in that output. + */ +public final class SchematronOutputConfig { + + /** + * Static output configuration - includes only static rules. + * Use when external services are not available. + */ + public static final SchematronOutputConfig STATIC = + new SchematronOutputConfig("static", EnumSet.of(RuleNature.STATIC)); + + /** + * Dynamic output configuration - includes all rules (static and dynamic). + * Use for full validation when external services are available. + */ + public static final SchematronOutputConfig DYNAMIC = + new SchematronOutputConfig("dynamic", EnumSet.allOf(RuleNature.class)); + + private final String folderName; + private final EnumSet ruleNatures; + + public SchematronOutputConfig(String folderName, EnumSet ruleNatures) { + this.folderName = folderName; + this.ruleNatures = ruleNatures; + } + + public String folderName() { + return this.folderName; + } + + public EnumSet ruleNatures() { + return this.ruleNatures; + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronPattern.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronPattern.java new file mode 100644 index 00000000..b31f9e16 --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronPattern.java @@ -0,0 +1,144 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import eu.europa.ted.efx.model.rules.RuleNature; +import eu.europa.ted.efx.model.rules.RuleSet; +import eu.europa.ted.efx.model.rules.ValidationRule; +import eu.europa.ted.efx.model.rules.ValidationStage; +import eu.europa.ted.efx.model.variables.Variable; + +/** + * Represents a Schematron <pattern> element. + * Each pattern is specific to a stage AND a notice type, containing only + * the assertions that apply to that notice type. + */ +public class SchematronPattern { + private final String stage; + private final String noticeType; + private final List variables; + private final List rules; + + /** + * Creates a pattern for a specific stage and notice type combination. + * Only includes rules/assertions that apply to the given notice type. + * + * @param validationStage The validation stage + * @param noticeType The notice type to filter by + */ + public SchematronPattern(ValidationStage validationStage, String noticeType) { + this.stage = validationStage.getName(); + this.noticeType = noticeType; + this.variables = collectVariables(validationStage); + this.rules = createRules(validationStage, noticeType); + } + + private static List collectVariables(ValidationStage stage) { + List vars = new ArrayList<>(); + for (Variable var : stage.getVariables()) { + vars.add(new SchematronLet(var.name, var.initializationExpression.getScript())); + } + for (RuleSet ruleSet : stage.getRuleSets()) { + for (Variable var : ruleSet.getStageVariables()) { + vars.add(new SchematronLet(var.name, var.initializationExpression.getScript())); + } + } + return vars; + } + + private static List createRules(ValidationStage stage, String noticeType) { + List rules = new ArrayList<>(); + for (RuleSet ruleSet : stage.getRuleSets()) { + SchematronRule rule = new SchematronRule(ruleSet, noticeType); + if (rule.hasTests()) { + rules.add(rule); + } + } + return rules; + } + + /** + * Returns all notice types referenced in the given stage. + * Used by SchematronGenerator to determine which patterns to create. + */ + public static Set getNoticeTypesInStage(ValidationStage stage) { + Set types = new LinkedHashSet<>(); + for (RuleSet ruleSet : stage.getRuleSets()) { + for (ValidationRule rule : ruleSet) { + if (rule.getNoticeSubtypes() != null) { + types.addAll(rule.getNoticeSubtypes().asList()); + } + } + ValidationRule fallback = ruleSet.getFallbackRule(); + if (fallback != null && fallback.getNoticeSubtypes() != null) { + types.addAll(fallback.getNoticeSubtypes().asList()); + } + } + return types; + } + + /** Used by pattern.ftl */ + public String getId() { + return "validation-stage-" + this.stage + "-" + this.noticeType; + } + + public String getStage() { + return this.stage; + } + + public String getNoticeType() { + return this.noticeType; + } + + /** Used by pattern.ftl */ + public List getVariables() { + return this.variables; + } + + /** Used by pattern.ftl */ + public List getRules() { + return this.rules; + } + + /** Returns true if this pattern has at least one rule with assertions. */ + public boolean hasRules() { + return !this.rules.isEmpty(); + } + + /** + * Returns true if this pattern has at least one rule with tests matching the given rule natures. + * + * @param ruleNatures The set of rule natures to include + * @return true if any rule has tests of the included natures + */ + public boolean hasRulesFor(Set ruleNatures) { + return this.rules.stream().anyMatch(r -> r.hasTestsFor(ruleNatures)); + } + + /** Computed on-demand from rules. */ + public Map getDiagnostics() { + Map diagnostics = new LinkedHashMap<>(); + for (SchematronRule rule : this.rules) { + diagnostics.putAll(rule.getDiagnostics()); + } + return diagnostics; + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronPhase.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronPhase.java new file mode 100644 index 00000000..47b3bca2 --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronPhase.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a Schematron <phase> element. + */ +public class SchematronPhase { + private final String noticeType; + private final List activePatterns = new ArrayList<>(); + + public SchematronPhase(String noticeType) { + this.noticeType = noticeType; + } + + /** Used by complete-validation.ftl */ + public String getId() { + return "eforms-" + this.noticeType; + } + + /** Used by complete-validation.ftl */ + public List getActivePatterns() { + return this.activePatterns; + } + + public void addActivePattern(String patternId) { + this.activePatterns.add(patternId); + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronReport.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronReport.java new file mode 100644 index 00000000..e3454ecf --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronReport.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import eu.europa.ted.efx.model.Context; +import eu.europa.ted.efx.model.rules.ValidationRule; + +/** + * Represents a Schematron <report> element. + * Fires when the test expression evaluates to true. + */ +public class SchematronReport extends SchematronTest { + + public SchematronReport(ValidationRule rule, Context ruleContext) { + super(rule, ruleContext); + } + + @Override + public String getElementName() { + return "report"; + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronRule.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronRule.java new file mode 100644 index 00000000..1a1d3c32 --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronRule.java @@ -0,0 +1,135 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import eu.europa.ted.efx.model.Context; +import eu.europa.ted.efx.model.rules.RuleNature; +import eu.europa.ted.efx.model.rules.ReportRule; +import eu.europa.ted.efx.model.rules.RuleSet; +import eu.europa.ted.efx.model.rules.ValidationRule; +import eu.europa.ted.efx.model.variables.Variable; + +/** + * Represents a Schematron <rule> element. + */ +public class SchematronRule { + private final List variables; + private final List tests; + private final Context context; + + /** + * Creates a SchematronRule containing only tests that apply to the given notice type. + * + * @param ruleSet The source rule set + * @param noticeType The notice type to filter tests by + */ + public SchematronRule(RuleSet ruleSet, String noticeType) { + this.context = ruleSet.getContext(); + + List variables = new ArrayList<>(); + List tests = new ArrayList<>(); + + for (Variable var : ruleSet.getLocalVariables()) { + variables.add(new SchematronLet(var.name, var.initializationExpression.getScript())); + } + + for (ValidationRule validationRule : ruleSet) { + if (appliesToNoticeType(validationRule, noticeType)) { + if (validationRule instanceof ReportRule) { + tests.add(new SchematronReport(validationRule, this.context)); + } else { + tests.add(new SchematronAssert(validationRule, this.context)); + } + } + } + + ValidationRule fallback = ruleSet.getFallbackRule(); + if (fallback != null && appliesToNoticeType(fallback, noticeType)) { + if (fallback instanceof ReportRule) { + tests.add(new SchematronReport(fallback, this.context)); + } else { + tests.add(new SchematronAssert(fallback, this.context)); + } + } + + this.variables = variables; + this.tests = tests; + } + + private static boolean appliesToNoticeType(ValidationRule rule, String noticeType) { + return rule.getNoticeSubtypes() != null + && rule.getNoticeSubtypes().asList().contains(noticeType); + } + + /** Used by pattern.ftl */ + public String getContext() { + return this.context.absolutePath().getScript(); + } + + /** Used by pattern.ftl */ + public List getVariables() { + return this.variables; + } + + /** Used by pattern.ftl */ + public List getTests() { + return this.tests; + } + + /** Returns true if this rule has at least one test. */ + public boolean hasTests() { + return !this.tests.isEmpty(); + } + + /** + * Returns true if this rule has at least one test matching the given rule natures. + * + * @param ruleNatures The set of rule natures to include + * @return true if any test's nature is in the included set + */ + public boolean hasTestsFor(Set ruleNatures) { + return this.tests.stream().anyMatch(t -> ruleNatures.contains(t.getRuleNature())); + } + + /** + * Returns true if this rule has at least one test matching the given tags. + * Used by FreeMarker templates. + * + * @param tags The list of tags to include + * @return true if any test's tag is in the list + */ + public boolean hasTestsForTags(List tags) { + return this.tests.stream().anyMatch(t -> tags.contains(t.getTag())); + } + + /** + * Returns diagnostics for tests that need them (where subject differs from context). + */ + public Map getDiagnostics() { + Map diagnostics = new LinkedHashMap<>(); + for (SchematronTest test : this.tests) { + SchematronDiagnostic diagnostic = test.getDiagnostic(); + if (diagnostic != null) { + diagnostics.put(diagnostic.getId(), diagnostic); + } + } + return diagnostics; + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronSchema.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronSchema.java new file mode 100644 index 00000000..fa33d472 --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronSchema.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a Schematron schema (the root <schema> element). + * This is used for the complete-validation.sch master file. + */ +public class SchematronSchema { + private final String title; + private final List globalVariables = new ArrayList<>(); + private final List diagnostics = new ArrayList<>(); + private final List phases = new ArrayList<>(); + private final List includes = new ArrayList<>(); + + public SchematronSchema(String title) { + this.title = title; + } + + /** Used by complete-validation.ftl */ + public String getTitle() { + return this.title; + } + + /** Used by complete-validation.ftl */ + public List getGlobalVariables() { + return this.globalVariables; + } + + /** Used by complete-validation.ftl */ + public List getDiagnostics() { + return this.diagnostics; + } + + /** Used by complete-validation.ftl */ + public List getPhases() { + return this.phases; + } + + /** Used by complete-validation.ftl */ + public List getIncludes() { + return this.includes; + } + + public void addGlobalVariable(SchematronLet variable) { + this.globalVariables.add(variable); + } + + public void addDiagnostic(SchematronDiagnostic diagnostic) { + this.diagnostics.add(diagnostic); + } + + public void addPhase(SchematronPhase phase) { + this.phases.add(phase); + } + + public void addInclude(String href) { + this.includes.add(href); + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronTest.java b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronTest.java new file mode 100644 index 00000000..a408af5d --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/schematron/SchematronTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.schematron; + +import eu.europa.ted.efx.model.Context; +import eu.europa.ted.efx.model.rules.RuleNature; +import eu.europa.ted.efx.model.rules.ValidationRule; + +/** + * Base class for Schematron test elements (assert and report). + * Both have a test attribute containing an XPath expression. + */ +public abstract class SchematronTest { + protected final ValidationRule rule; + protected final SchematronDiagnostic diagnostic; + + protected SchematronTest(ValidationRule rule, Context ruleContext) { + this.rule = rule; + this.diagnostic = rule.getSubject().symbol().equals(ruleContext.symbol()) + ? null + : new SchematronDiagnostic(rule.getSubject(), ruleContext); + } + + /** Used by pattern.ftl */ + public String getId() { + return this.rule.getId(); + } + + /** Used by pattern.ftl */ + public String getRole() { + return this.rule.getSeverity() != null + ? this.rule.getSeverity().toString().toLowerCase() + : "error"; + } + + /** Used by pattern.ftl */ + public String getTest() { + return this.rule.getInvertedConditionOrExpressionCombination().getScript(); + } + + /** Used by pattern.ftl */ + public String getMessage() { + return "rule|text|" + this.rule.getId(); + } + + /** Used by pattern.ftl */ + public SchematronDiagnostic getDiagnostic() { + return this.diagnostic; + } + + /** Returns the nature of the underlying validation rule */ + public RuleNature getRuleNature() { + return this.rule.getNature(); + } + + /** Used by pattern.ftl - returns the tag for filtering (derived from rule nature) */ + public String getTag() { + return this.rule.getNature().name(); + } + + /** Used by pattern.ftl - returns the Schematron element name ("assert" or "report") */ + public abstract String getElementName(); +} diff --git a/src/main/java/eu/europa/ted/efx/EfxTranslator.java b/src/main/java/eu/europa/ted/efx/EfxTranslator.java index 5a4cf6da..454327f7 100644 --- a/src/main/java/eu/europa/ted/efx/EfxTranslator.java +++ b/src/main/java/eu/europa/ted/efx/EfxTranslator.java @@ -9,13 +9,14 @@ * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under - * the Lic + * the Licence. */ package eu.europa.ted.efx; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; +import java.util.Map; import eu.europa.ted.efx.component.EfxTranslatorFactory; import eu.europa.ted.efx.interfaces.TranslatorDependencyFactory; @@ -168,4 +169,105 @@ public static String translateTemplate(final TranslatorDependencyFactory depende } //#endregion Translate EFX templates ---------------------------------------- + + //#region Translate EFX rules ----------------------------------------------- + + /** + * Instantiates an EFX rules translator and translates the EFX rules contained in the given file. + * + * @param dependencyFactory A {@link TranslatorDependencyFactory} to be used for instantiating the + * dependencies of the EFX rules translator. + * @param sdkVersion The version of the eForms SDK that defines the EFX grammar used by the EFX + * rules to be translated. + * @param pathname The path to the file containing the EFX rules to translate. + * @param options The options to be used by the EFX rules translator. + * @return A map where keys are output file paths (relative) and values are the generated Schematron content. + * @throws IOException If the file cannot be read. + * @throws InstantiationException If the EFX rules translator cannot be instantiated. + */ + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final Path pathname, TranslatorOptions options) + throws IOException, InstantiationException { + return EfxTranslatorFactory.getEfxRulesTranslator(sdkVersion, dependencyFactory, options) + .translateRules(pathname, options); + } + + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final Path pathname) + throws IOException, InstantiationException { + return translateRules(dependencyFactory, sdkVersion, pathname, defaultOptions); + } + + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final String qualifier, final Path pathname, TranslatorOptions options) + throws IOException, InstantiationException { + return EfxTranslatorFactory.getEfxRulesTranslator(sdkVersion, qualifier, dependencyFactory, options) + .translateRules(pathname, options); + } + + /** + * Instantiates an EFX rules translator and translates the given EFX rules. + * + * @param dependencyFactory A {@link TranslatorDependencyFactory} to be used for instantiating the + * dependencies of the EFX rules translator. + * @param sdkVersion The version of the eForms SDK that defines the EFX grammar used by the EFX + * rules to be translated. + * @param rules A string containing the EFX rules to translate. + * @param options The options to be used by the EFX rules translator. + * @return A map where keys are output file paths (relative) and values are the generated Schematron content. + * @throws InstantiationException If the EFX rules translator cannot be instantiated. + */ + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final String rules, TranslatorOptions options) + throws InstantiationException { + return translateRules(dependencyFactory, sdkVersion, "", rules, options); + } + + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final String rules) + throws InstantiationException { + return translateRules(dependencyFactory, sdkVersion, rules, defaultOptions); + } + + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final String qualifier, final String rules, TranslatorOptions options) + throws InstantiationException { + return EfxTranslatorFactory.getEfxRulesTranslator(sdkVersion, qualifier, dependencyFactory, options) + .translateRules(rules, options); + } + + /** + * Instantiates an EFX rules translator and translates the EFX rules contained in the given + * InputStream. + * + * @param dependencyFactory A {@link TranslatorDependencyFactory} to be used for instantiating the + * dependencies of the EFX rules translator. + * @param sdkVersion The version of the eForms SDK that defines the EFX grammar used by the EFX + * rules to be translated. + * @param stream An InputStream containing the EFX rules to be translated. + * @param options The options to be used by the EFX rules translator. + * @return A map where keys are output file paths (relative) and values are the generated Schematron content. + * @throws IOException If the InputStream cannot be read. + * @throws InstantiationException If the EFX rules translator cannot be instantiated. + */ + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final InputStream stream, TranslatorOptions options) + throws IOException, InstantiationException { + return translateRules(dependencyFactory, sdkVersion, "", stream, options); + } + + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final InputStream stream) + throws IOException, InstantiationException { + return translateRules(dependencyFactory, sdkVersion, stream, defaultOptions); + } + + public static Map translateRules(final TranslatorDependencyFactory dependencyFactory, + final String sdkVersion, final String qualifier, final InputStream stream, TranslatorOptions options) + throws IOException, InstantiationException { + return EfxTranslatorFactory.getEfxRulesTranslator(sdkVersion, qualifier, dependencyFactory, options) + .translateRules(stream, options); + } + + //#endregion Translate EFX rules -------------------------------------------- } diff --git a/src/main/java/eu/europa/ted/efx/component/EfxTranslatorFactory.java b/src/main/java/eu/europa/ted/efx/component/EfxTranslatorFactory.java index 9bc54785..00f21ce1 100644 --- a/src/main/java/eu/europa/ted/efx/component/EfxTranslatorFactory.java +++ b/src/main/java/eu/europa/ted/efx/component/EfxTranslatorFactory.java @@ -3,12 +3,14 @@ import eu.europa.ted.eforms.sdk.component.SdkComponentFactory; import eu.europa.ted.eforms.sdk.component.SdkComponentType; import eu.europa.ted.efx.interfaces.EfxExpressionTranslator; +import eu.europa.ted.efx.interfaces.EfxRulesTranslator; import eu.europa.ted.efx.interfaces.EfxTemplateTranslator; import eu.europa.ted.efx.interfaces.MarkupGenerator; import eu.europa.ted.efx.interfaces.ScriptGenerator; import eu.europa.ted.efx.interfaces.SymbolResolver; import eu.europa.ted.efx.interfaces.TranslatorDependencyFactory; import eu.europa.ted.efx.interfaces.TranslatorOptions; +import eu.europa.ted.efx.interfaces.ValidatorGenerator; public class EfxTranslatorFactory extends SdkComponentFactory { public static final EfxTranslatorFactory INSTANCE = new EfxTranslatorFactory(); @@ -51,4 +53,22 @@ public static EfxTemplateTranslator getEfxTemplateTranslator(final String sdkVer SdkComponentType.EFX_TEMPLATE_TRANSLATOR, qualifier, EfxTemplateTranslator.class, markupGenerator, symbolResolver, scriptGenerator, factory.createErrorListener()); } + + public static EfxRulesTranslator getEfxRulesTranslator(final String sdkVersion, + final TranslatorDependencyFactory factory, TranslatorOptions options) throws InstantiationException { + return getEfxRulesTranslator(sdkVersion, "", factory, options); + } + + public static EfxRulesTranslator getEfxRulesTranslator(final String sdkVersion, + final String qualifier, final TranslatorDependencyFactory factory, TranslatorOptions options) + throws InstantiationException { + + ValidatorGenerator validatorGenerator = factory.createValidatorGenerator(sdkVersion, qualifier, options); + SymbolResolver symbolResolver = factory.createSymbolResolver(sdkVersion, qualifier); + ScriptGenerator scriptGenerator = factory.createScriptGenerator(sdkVersion, qualifier, options); + + return EfxTranslatorFactory.INSTANCE.getComponentImpl(sdkVersion, + SdkComponentType.EFX_RULES_TRANSLATOR, qualifier, EfxRulesTranslator.class, + validatorGenerator, symbolResolver, scriptGenerator, factory.createErrorListener()); + } } diff --git a/src/main/java/eu/europa/ted/efx/interfaces/EfxExpressionTranslator.java b/src/main/java/eu/europa/ted/efx/interfaces/EfxExpressionTranslator.java index 8e2dc0d6..11d1b92e 100644 --- a/src/main/java/eu/europa/ted/efx/interfaces/EfxExpressionTranslator.java +++ b/src/main/java/eu/europa/ted/efx/interfaces/EfxExpressionTranslator.java @@ -9,7 +9,7 @@ * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under - * the Lic + * the Licence. */ package eu.europa.ted.efx.interfaces; diff --git a/src/main/java/eu/europa/ted/efx/interfaces/EfxRulesTranslator.java b/src/main/java/eu/europa/ted/efx/interfaces/EfxRulesTranslator.java new file mode 100644 index 00000000..cd6e1004 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/interfaces/EfxRulesTranslator.java @@ -0,0 +1,95 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.interfaces; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Map; + +import eu.europa.ted.efx.EfxTranslatorOptions; + +/** + * Defines the API of an EFX rules translator. + * + * An EFX rules translator converts EFX rules files into Schematron validation files. + * It generates multiple .sch pattern files, a complete-validation.sch master file, + * and a schematrons.json metadata file. + * + * Note that the behaviour of the translator is defined once during its instantiation using a + * TranslatorDependencyFactory. + */ +public interface EfxRulesTranslator extends EfxExpressionTranslator { + + /** + * Translate the EFX rules stored in a file, given the pathname of the file. + * + * @param pathname The path and filename of the EFX rules file to translate. + * @param options The options to be used by the EFX rules translator. + * @return A map where keys are output file paths (relative) and values are the generated content. + * @throws IOException If the file cannot be read. + */ + Map translateRules(Path pathname, TranslatorOptions options) throws IOException; + + /** + * Translate the EFX rules stored in a file, given the pathname of the file. + * + * @param pathname The path and filename of the EFX rules file to translate. + * @return A map where keys are output file paths (relative) and values are the generated content. + * @throws IOException If the file cannot be read. + */ + default Map translateRules(Path pathname) throws IOException { + return translateRules(pathname, EfxTranslatorOptions.DEFAULT); + } + + /** + * Translate the EFX rules stored in the given string. + * + * @param rules A string containing EFX rules to be translated. + * @param options The options to be used by the EFX rules translator. + * @return A map where keys are output file paths (relative) and values are the generated content. + */ + Map translateRules(String rules, TranslatorOptions options); + + /** + * Translate the EFX rules stored in the given string. + * + * @param rules A string containing EFX rules to be translated. + * @return A map where keys are output file paths (relative) and values are the generated content. + */ + default Map translateRules(String rules) { + return translateRules(rules, EfxTranslatorOptions.DEFAULT); + } + + /** + * Translate the EFX rules given as an InputStream. + * + * @param stream An InputStream with the EFX rules to be translated. + * @param options The options to be used by the EFX rules translator. + * @return A map where keys are output file paths (relative) and values are the generated content. + * @throws IOException If the InputStream cannot be read. + */ + Map translateRules(InputStream stream, TranslatorOptions options) throws IOException; + + /** + * Translate the EFX rules given as an InputStream. + * + * @param stream An InputStream with the EFX rules to be translated. + * @return A map where keys are output file paths (relative) and values are the generated content. + * @throws IOException If the InputStream cannot be read. + */ + default Map translateRules(InputStream stream) throws IOException { + return translateRules(stream, EfxTranslatorOptions.DEFAULT); + } +} diff --git a/src/main/java/eu/europa/ted/efx/interfaces/EfxTemplateTranslator.java b/src/main/java/eu/europa/ted/efx/interfaces/EfxTemplateTranslator.java index 1fb49f75..95030fb4 100644 --- a/src/main/java/eu/europa/ted/efx/interfaces/EfxTemplateTranslator.java +++ b/src/main/java/eu/europa/ted/efx/interfaces/EfxTemplateTranslator.java @@ -9,7 +9,7 @@ * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under - * the Lic + * the Licence. */ package eu.europa.ted.efx.interfaces; diff --git a/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java b/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java index 0d435da9..a3c3a3d8 100644 --- a/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java +++ b/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java @@ -9,7 +9,7 @@ * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under - * the Lic + * the Licence. */ package eu.europa.ted.efx.interfaces; diff --git a/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java b/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java index 18083bf5..24051ab9 100644 --- a/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java +++ b/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java @@ -9,7 +9,7 @@ * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under - * the Lic + * the Licence. */ package eu.europa.ted.efx.interfaces; diff --git a/src/main/java/eu/europa/ted/efx/interfaces/SymbolResolver.java b/src/main/java/eu/europa/ted/efx/interfaces/SymbolResolver.java index 7c42332f..f56132fd 100644 --- a/src/main/java/eu/europa/ted/efx/interfaces/SymbolResolver.java +++ b/src/main/java/eu/europa/ted/efx/interfaces/SymbolResolver.java @@ -9,7 +9,7 @@ * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under - * the Lic + * the Licence. */ package eu.europa.ted.efx.interfaces; @@ -152,6 +152,14 @@ public PathExpression getRelativePathOfNode(final String nodeId, */ public List expandCodelist(final String codelistId); + /** + * Gets a list of all valid notice subtype IDs. + * Used for validating notice subtype references in EFX. + * + * @return List of notice type IDs (e.g., ["1", "2", ..., "40", "CEI", "E1", ..., "X02"]) + */ + public List getAllNoticeSubtypeIds(); + public String getFieldIdFromAlias(final String alias); public String getNodeIdFromAlias(final String alias); diff --git a/src/main/java/eu/europa/ted/efx/interfaces/TranslatorDependencyFactory.java b/src/main/java/eu/europa/ted/efx/interfaces/TranslatorDependencyFactory.java index 59ad652d..8e904713 100644 --- a/src/main/java/eu/europa/ted/efx/interfaces/TranslatorDependencyFactory.java +++ b/src/main/java/eu/europa/ted/efx/interfaces/TranslatorDependencyFactory.java @@ -9,7 +9,7 @@ * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under - * the Lic + * the Licence. */ package eu.europa.ted.efx.interfaces; @@ -48,11 +48,14 @@ public interface TranslatorDependencyFactory { * This method is called by the EFX translator to instantiate the ScriptGenerator it will use to * translate EFX expressions to the target script language. * - * @param sdkVersion The version of the SDK that contains the version of the EFX grammar that the - * EFX translator will attempt to translate. This is important as it defines the EFX - * language features that ScriptGenerator instance should be able to handle. - * @param qualifier Qualifier to choose between several implementations. - * @param options The options to be used by the ScriptGenerator. + * @param sdkVersion The version of the SDK that contains the version of the EFX + * grammar that the + * EFX translator will attempt to translate. This is important + * as it defines the EFX + * language features that ScriptGenerator instance should be + * able to handle. + * @param qualifier Qualifier to choose between several implementations. + * @param options The options to be used by the ScriptGenerator. * @return An instance of ScriptGenerator to be used by the EFX translator. */ public ScriptGenerator createScriptGenerator(String sdkVersion, String qualifier, TranslatorOptions options); @@ -72,6 +75,19 @@ public interface TranslatorDependencyFactory { */ public MarkupGenerator createMarkupGenerator(String sdkVersion, String qualifier, TranslatorOptions options); + /** + * Instantiates a validator generator. + * + * @param sdkVersion The version of the SDK for which the markup generator will + * be instantiated. + * @param qualifier The component qualifier to identify the implementation. + * @param options The validation options associated with this generator. + * @return The instantiated validator generator. + */ + ValidatorGenerator createValidatorGenerator(String sdkVersion, String qualifier, + TranslatorOptions options); + + /** * Creates an error listener instance. * diff --git a/src/main/java/eu/europa/ted/efx/interfaces/ValidatorGenerator.java b/src/main/java/eu/europa/ted/efx/interfaces/ValidatorGenerator.java new file mode 100644 index 00000000..00600501 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/interfaces/ValidatorGenerator.java @@ -0,0 +1,45 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.interfaces; + +import java.io.IOException; +import java.util.Map; + +import eu.europa.ted.efx.model.rules.CompleteValidation; + +/** + * Interface for generating validation output from the intermediate validation model. + * + * This interface abstracts the generation of validation files from the intermediate + * representation (ValidationStage) produced by the EFX Rules translator. Different + * implementations can produce different output formats (e.g., Schematron, JavaScript). + * + * The typical workflow is: + * 1. EFX Rules are parsed and translated into an intermediate model (List of ValidationStage) + * 2. The ValidatorGenerator transforms this intermediate model into the target format + */ +public interface ValidatorGenerator { + + /** + * Generates validation output files from the intermediate model. + * + * @param completeValidation The complete validation model containing stages, + * global variables, and notice subtypes. + * @return A map of filename to file content for all generated validation files. + * @throws IOException If an error occurs during file generation. + */ + Map generateOutput( + CompleteValidation completeValidation + ) throws IOException; +} diff --git a/src/main/java/eu/europa/ted/efx/model/CallStack.java b/src/main/java/eu/europa/ted/efx/model/CallStack.java index ddba7ac3..77907b5a 100644 --- a/src/main/java/eu/europa/ted/efx/model/CallStack.java +++ b/src/main/java/eu/europa/ted/efx/model/CallStack.java @@ -2,9 +2,11 @@ import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Stack; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.antlr.v4.runtime.misc.ParseCancellationException; @@ -232,6 +234,12 @@ public Identifiers getGlobals() { return globals; } + public List getGlobalVariables() { + return this.globalIdentifierRegistry.values().stream() + .filter(Variable.class::isInstance) + .map(Variable.class::cast) + .collect(Collectors.toList()); + } /** * Gets the value of a parameter. diff --git a/src/main/java/eu/europa/ted/efx/model/rules/AssertRule.java b/src/main/java/eu/europa/ted/efx/model/rules/AssertRule.java new file mode 100644 index 00000000..3c55c2d6 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/AssertRule.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +public class AssertRule extends ValidationRule { + public AssertRule() { + super(); + } + + public AssertRule(ValidationRule other) { + this.id = other.id; + this.subject = other.subject; + this.severity = other.severity; + this.condition = other.condition; + this.invertedCondition = other.invertedCondition; + this.expression = other.expression; + this.invertedConditionOrExpression = other.invertedConditionOrExpression; + this.noticeSubtypes = other.noticeSubtypes; + } +} \ No newline at end of file diff --git a/src/main/java/eu/europa/ted/efx/model/rules/CompleteValidation.java b/src/main/java/eu/europa/ted/efx/model/rules/CompleteValidation.java new file mode 100644 index 00000000..94d62e61 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/CompleteValidation.java @@ -0,0 +1,91 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +import java.util.ArrayList; +import java.util.List; + +import eu.europa.ted.efx.model.ParsedEntity; +import eu.europa.ted.efx.model.variables.Variable; + +public class CompleteValidation implements ParsedEntity { + + List globalVariables = new ArrayList<>(); + + List stages = new ArrayList<>(); + + List noticeSubtypes = new ArrayList<>(); + + public List getNoticeSubtypes() { + return new ArrayList<>(this.noticeSubtypes); + } + + public List getStages() { + return stages; + } + + public void addStage(ValidationStage stage) { + this.stages.add(stage); + } + + public void addNoticeSubtype(String noticeSubtype) { + if (!this.noticeSubtypes.contains(noticeSubtype)) { + this.noticeSubtypes.add(noticeSubtype); + sortNoticeSubtypes(); + } + } + + public void addNoticeSubtypes(List noticeSubtypes) { + boolean added = false; + for (String subtype : noticeSubtypes) { + if (!this.noticeSubtypes.contains(subtype)) { + this.noticeSubtypes.add(subtype); + added = true; + } + } + if (added) { + sortNoticeSubtypes(); + } + } + + public void addGlobalVariable(Variable variable) { + this.globalVariables.add(variable); + } + + public List getGlobalVariables() { + return new ArrayList<>(this.globalVariables); + } + + private void sortNoticeSubtypes() { + this.noticeSubtypes.sort(this::compareNoticeSubtype); + } + + private int compareNoticeSubtype(String a, String b) { + boolean aNum = isNumeric(a); + boolean bNum = isNumeric(b); + if (aNum && bNum) { + return Integer.compare(Integer.parseInt(a), Integer.parseInt(b)); + } + return a.compareTo(b); + } + + private boolean isNumeric(String value) { + try { + Integer.parseInt(value); + return true; + } catch (NumberFormatException ex) { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/europa/ted/efx/model/rules/NoticeSubtypeRange.java b/src/main/java/eu/europa/ted/efx/model/rules/NoticeSubtypeRange.java new file mode 100644 index 00000000..5fd9ccbe --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/NoticeSubtypeRange.java @@ -0,0 +1,102 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +import java.util.ArrayList; +import java.util.List; + +import eu.europa.ted.efx.model.ParsedEntity; + +public class NoticeSubtypeRange implements ParsedEntity, Iterable { + + private final List noticeSubtypes; + + public NoticeSubtypeRange(String rangeString, List validNoticeSubtypes) { + + this.noticeSubtypes = new ArrayList<>(); + + if (validNoticeSubtypes == null) { + validNoticeSubtypes = List.of(); + } + + rangeString = (rangeString == null) ? "" : rangeString.trim(); + + if (rangeString == "*" || rangeString.equalsIgnoreCase("ANY")) { + noticeSubtypes.addAll(validNoticeSubtypes); + return; + } + + for (String item : rangeString.trim().split("\\s*,\\s*")) { + if (item.isEmpty()) { + continue; + } + + String[] parts = item.split("\\s*-\\s*", -1); + switch (parts.length) { + case 1: { + int idx = validNoticeSubtypes.indexOf(parts[0]); + if (idx < 0) { + throw new IllegalArgumentException( + String.format("Invalid notice type ID '%s' in compressed list '%s'", + parts[0], rangeString)); + } + noticeSubtypes.add(validNoticeSubtypes.get(idx)); + break; + } + case 2: { + int startIdx = validNoticeSubtypes.indexOf(parts[0]); + if (startIdx < 0) { + throw new IllegalArgumentException( + String.format("Invalid notice subtype '%s' in range '%s-%s'.", + parts[0], parts[0], parts[1])); + } + int endIdx = validNoticeSubtypes.indexOf(parts[1]); + if (endIdx < 0) { + throw new IllegalArgumentException( + String.format("Invalid notice subtype '%s' in range '%s-%s'.", + parts[1], parts[0], parts[1])); + } + if (startIdx > endIdx) { + throw new IllegalArgumentException( + String.format("Notice subtype range '%s-%s' is not in ascending order.", parts[0], + parts[1])); + } + + for (int i = startIdx; i <= endIdx; i++) { + noticeSubtypes.add(validNoticeSubtypes.get(i)); + } + break; + } + default: + throw new IllegalArgumentException( + String.format("Invalid notice subtype token '%s'.", + item)); + } + } + + } + + public List asList() { + return List.copyOf(noticeSubtypes); + } + + public int size() { + return noticeSubtypes.size(); + } + + @Override + public java.util.Iterator iterator() { + return noticeSubtypes.iterator(); + } +} diff --git a/src/main/java/eu/europa/ted/efx/model/rules/ReportRule.java b/src/main/java/eu/europa/ted/efx/model/rules/ReportRule.java new file mode 100644 index 00000000..fdd40f45 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/ReportRule.java @@ -0,0 +1,30 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +public class ReportRule extends ValidationRule { + public ReportRule() { + super(); + } + public ReportRule(ValidationRule other) { + this.id = other.id; + this.subject = other.subject; + this.severity = other.severity; + this.condition = other.condition; + this.invertedCondition = other.invertedCondition; + this.expression = other.expression; + this.invertedConditionOrExpression = other.invertedConditionOrExpression; + this.noticeSubtypes = other.noticeSubtypes; + } +} \ No newline at end of file diff --git a/src/main/java/eu/europa/ted/efx/model/rules/RuleNature.java b/src/main/java/eu/europa/ted/efx/model/rules/RuleNature.java new file mode 100644 index 00000000..87cc18a5 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/RuleNature.java @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +public enum RuleNature { + STATIC, + DYNAMIC +} \ No newline at end of file diff --git a/src/main/java/eu/europa/ted/efx/model/rules/RuleSet.java b/src/main/java/eu/europa/ted/efx/model/rules/RuleSet.java new file mode 100644 index 00000000..ef7c3c6d --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/RuleSet.java @@ -0,0 +1,84 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import eu.europa.ted.efx.model.Context; +import eu.europa.ted.efx.model.ParsedEntity; +import eu.europa.ted.efx.model.variables.Variable; + +public class RuleSet implements ParsedEntity, Iterable { + + Context context; + List rules; + ValidationRule fallbackRule; + + List stageVariables; + List localVariables; + + public RuleSet() { + this.context = null; + this.rules = new ArrayList<>(); + this.stageVariables = new ArrayList<>(); + this.localVariables = new ArrayList<>(); + this.fallbackRule = null; + } + + public void setContext(Context context, Variable contextVariable) { + this.context = context; + if (contextVariable != null) { + this.addVariable(contextVariable); + } + } + + public void addRule(ValidationRule rule) { + this.rules.add(rule); + } + + public void setFallbackRule(ValidationRule fallbackRule) { + this.fallbackRule = fallbackRule; + } + + public void addVariable(Variable variable) { + if (this.context == null) { + this.stageVariables.add(variable); + } else { + this.localVariables.add(variable); + } + } + + @Override + public Iterator iterator() { + return this.rules.iterator(); + } + + public Context getContext() { + return this.context; + } + + public List getStageVariables() { + return this.stageVariables; + } + + public List getLocalVariables() { + return this.localVariables; + } + + public ValidationRule getFallbackRule() { + return this.fallbackRule; + } +} \ No newline at end of file diff --git a/src/main/java/eu/europa/ted/efx/model/rules/RuleSeverity.java b/src/main/java/eu/europa/ted/efx/model/rules/RuleSeverity.java new file mode 100644 index 00000000..d75a8cec --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/RuleSeverity.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +public enum RuleSeverity { + ERROR, + WARNING, + INFO; + + public static RuleSeverity fromString(String value) { + if (value == null) { + throw new IllegalArgumentException("RuleSeverity value cannot be null"); + } + try { + return RuleSeverity.valueOf(value.trim().toUpperCase()); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("Unknown RuleSeverity: " + value, ex); + } + } +} diff --git a/src/main/java/eu/europa/ted/efx/model/rules/ValidationRule.java b/src/main/java/eu/europa/ted/efx/model/rules/ValidationRule.java new file mode 100644 index 00000000..0c8bf07a --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/ValidationRule.java @@ -0,0 +1,94 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +import eu.europa.ted.efx.model.Context; +import eu.europa.ted.efx.model.ParsedEntity; +import eu.europa.ted.efx.model.expressions.scalar.BooleanExpression; + +public class ValidationRule implements ParsedEntity { + + String id; + Context subject; + RuleSeverity severity; + RuleNature nature = RuleNature.STATIC; + BooleanExpression condition; + BooleanExpression invertedCondition; + BooleanExpression expression; + BooleanExpression invertedConditionOrExpression; + NoticeSubtypeRange noticeSubtypes; + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return this.id; + } + + public void setSubject(Context subject) { + this.subject = subject; + } + public Context getSubject() { + return this.subject; + } + + public void setSeverity(RuleSeverity severity) { + this.severity = severity; + } + public RuleSeverity getSeverity() { + return this.severity; + } + + public void setNature(RuleNature nature) { + this.nature = nature; + } + public RuleNature getNature() { + return this.nature; + } + + public void setCondition(BooleanExpression condition, BooleanExpression invertedCondition, BooleanExpression invertedConditionOrExpression) { + this.condition = condition; + this.invertedCondition = invertedCondition; + this.invertedConditionOrExpression = invertedConditionOrExpression; + } + + public BooleanExpression getCondition() { + return this.condition; + } + + public BooleanExpression getInvertedCondition() { + return this.invertedCondition; + } + + public void setExpression(BooleanExpression expression, BooleanExpression invertedConditionOrExpression) { + this.expression = expression; + this.invertedConditionOrExpression = invertedConditionOrExpression; + } + + public BooleanExpression getExpression() { + return this.expression; + } + + public BooleanExpression getInvertedConditionOrExpressionCombination() { + return this.invertedConditionOrExpression; + } + + public void setNoticeSubtypes(NoticeSubtypeRange noticeSubtypes) { + this.noticeSubtypes = noticeSubtypes; + } + public NoticeSubtypeRange getNoticeSubtypes() { + return this.noticeSubtypes; + } +} diff --git a/src/main/java/eu/europa/ted/efx/model/rules/ValidationStage.java b/src/main/java/eu/europa/ted/efx/model/rules/ValidationStage.java new file mode 100644 index 00000000..244b3251 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/model/rules/ValidationStage.java @@ -0,0 +1,53 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.model.rules; + +import java.util.ArrayList; +import java.util.List; + +import eu.europa.ted.efx.model.ParsedEntity; +import eu.europa.ted.efx.model.variables.Variable; + +public class ValidationStage implements ParsedEntity { + + String name; + List ruleSets; + List variables; + + public ValidationStage(String name) { + this.name = name; + this.ruleSets = new ArrayList<>(); + this.variables = new ArrayList<>(); + } + + public void addRuleSet(RuleSet ruleSet) { + this.ruleSets.add(ruleSet); + } + + public void addVariable(Variable variable) { + this.variables.add(variable); + } + + public String getName() { + return this.name; + } + + public List getRuleSets() { + return this.ruleSets; + } + + public List getVariables() { + return this.variables; + } +} \ No newline at end of file diff --git a/src/main/java/eu/europa/ted/efx/sdk1/entity/SdkNoticeSubtypeV1.java b/src/main/java/eu/europa/ted/efx/sdk1/entity/SdkNoticeSubtypeV1.java new file mode 100644 index 00000000..b8f2c689 --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/sdk1/entity/SdkNoticeSubtypeV1.java @@ -0,0 +1,21 @@ +package eu.europa.ted.efx.sdk1.entity; + +import com.fasterxml.jackson.databind.JsonNode; +import eu.europa.ted.eforms.sdk.component.SdkComponent; +import eu.europa.ted.eforms.sdk.component.SdkComponentType; +import eu.europa.ted.eforms.sdk.entity.SdkNoticeSubtype; + +/** + * Represents a notice subtype from the SDK's notice-types.json file. + */ +@SdkComponent(versions = {"1"}, componentType = SdkComponentType.NOTICE_TYPE) +public class SdkNoticeSubtypeV1 extends SdkNoticeSubtype { + + public SdkNoticeSubtypeV1(String subTypeId, String documentType, String type) { + super(subTypeId, documentType, type); + } + + public SdkNoticeSubtypeV1(JsonNode json) { + super(json); + } +} diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java index 67f54bb3..4aeeba67 100644 --- a/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java +++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java @@ -1720,14 +1720,16 @@ public void exitLateBoundScalar(LateBoundScalarContext ctx) { assert false: "This should have been handled by the preprocessor: " + ctx.getText() +". Check any changes that you might have made in the EFX grammar that may have broken this assumption."; } - private static String textTypeName = getLexerSymbol(EfxLexer.Text); - private static String booleanTypeName = getLexerSymbol(EfxLexer.Indicator); - private static String numericTypeName = getLexerSymbol(EfxLexer.Number); - private static String dateTypeName = getLexerSymbol(EfxLexer.Date); - private static String timeTypeName = getLexerSymbol(EfxLexer.Time); - private static String durationTypeName = getLexerSymbol(EfxLexer.Measure); - - private static final Map eFormsToEfxTypeMap = Map.ofEntries( // + // Type name constants - made protected for reuse in subclasses + protected static String textTypeName = getLexerSymbol(EfxLexer.Text); + protected static String booleanTypeName = getLexerSymbol(EfxLexer.Indicator); + protected static String numericTypeName = getLexerSymbol(EfxLexer.Number); + protected static String dateTypeName = getLexerSymbol(EfxLexer.Date); + protected static String timeTypeName = getLexerSymbol(EfxLexer.Time); + protected static String durationTypeName = getLexerSymbol(EfxLexer.Measure); + + // Map from eForms field types to EFX type names - made protected for reuse in subclasses + protected static final Map eFormsToEfxTypeMap = Map.ofEntries( // entry(FieldTypes.ID.getName(), textTypeName), // entry(FieldTypes.ID_REF.getName(), textTypeName), // entry(FieldTypes.TEXT.getName(), textTypeName), // @@ -1747,7 +1749,8 @@ public void exitLateBoundScalar(LateBoundScalarContext ctx) { entry(FieldTypes.PHONE.getName(), textTypeName), // entry(FieldTypes.EMAIL.getName(), textTypeName)); - private static final Map, String> javaToEfxTypeMap = Map.ofEntries( + // Map from Java EfxDataType classes to EFX type names - made protected for reuse in subclasses + protected static final Map, String> javaToEfxTypeMap = Map.ofEntries( entry(EfxDataType.String.class, textTypeName), // entry(EfxDataType.Boolean.class, booleanTypeName), // entry(EfxDataType.Number.class, numericTypeName), // diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2.java new file mode 100644 index 00000000..ba57512b --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2.java @@ -0,0 +1,750 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.sdk2; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Map; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.europa.ted.eforms.sdk.component.SdkComponent; +import eu.europa.ted.eforms.sdk.component.SdkComponentType; +import eu.europa.ted.efx.interfaces.EfxRulesTranslator; +import eu.europa.ted.efx.interfaces.ScriptGenerator; +import eu.europa.ted.efx.interfaces.SymbolResolver; +import eu.europa.ted.efx.interfaces.TranslatorOptions; +import eu.europa.ted.efx.interfaces.ValidatorGenerator; +import eu.europa.ted.efx.model.Context.FieldContext; +import eu.europa.ted.efx.model.Context.NodeContext; +import eu.europa.ted.efx.model.expressions.TypedExpression; +import eu.europa.ted.efx.model.expressions.path.NodePathExpression; +import eu.europa.ted.efx.model.expressions.path.PathExpression; +import eu.europa.ted.efx.model.expressions.scalar.BooleanExpression; +import eu.europa.ted.efx.model.expressions.scalar.DateExpression; +import eu.europa.ted.efx.model.expressions.scalar.DurationExpression; +import eu.europa.ted.efx.model.expressions.scalar.NumericExpression; +import eu.europa.ted.efx.model.expressions.scalar.ScalarExpression; +import eu.europa.ted.efx.model.expressions.scalar.StringExpression; +import eu.europa.ted.efx.model.expressions.scalar.TimeExpression; +import eu.europa.ted.efx.model.rules.AssertRule; +import eu.europa.ted.efx.model.rules.CompleteValidation; +import eu.europa.ted.efx.model.rules.NoticeSubtypeRange; +import eu.europa.ted.efx.model.rules.ReportRule; +import eu.europa.ted.efx.model.rules.RuleSet; +import eu.europa.ted.efx.model.rules.RuleSeverity; +import eu.europa.ted.efx.model.rules.ValidationRule; +import eu.europa.ted.efx.model.rules.ValidationStage; +import eu.europa.ted.efx.model.types.FieldTypes; +import eu.europa.ted.efx.model.variables.Variable; +import eu.europa.ted.efx.sdk2.EfxParser.AnyNoticeTypesContext; +import eu.europa.ted.efx.sdk2.EfxParser.AsClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.AssertClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.ConditionalRuleContext; +import eu.europa.ted.efx.sdk2.EfxParser.ContextDeclarationContext; +import eu.europa.ted.efx.sdk2.EfxParser.ContextVariableInitializerContext; +import eu.europa.ted.efx.sdk2.EfxParser.FallbackRuleContext; +import eu.europa.ted.efx.sdk2.EfxParser.ForClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.GlobalVariableDeclarationContext; +import eu.europa.ted.efx.sdk2.EfxParser.InClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.OtherwiseAssertClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.OtherwiseReportClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.StageVariableDeclarationContext; +import eu.europa.ted.efx.sdk2.EfxParser.ReportClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.RuleSetContext; +import eu.europa.ted.efx.sdk2.EfxParser.SimpleRuleContext; +import eu.europa.ted.efx.sdk2.EfxParser.ValidationStageContext; +import eu.europa.ted.efx.sdk2.EfxParser.VariableInitializerContext; +import eu.europa.ted.efx.sdk2.EfxParser.WhenClauseContext; +import eu.europa.ted.efx.sdk2.EfxParser.WithClauseContext; + +/** + * EFX Rules translator for SDK version 2. + * + * This translator parses EFX Rules files and produces an intermediate model + * (List of ValidationStage) which is then passed to a ValidatorGenerator + * for output generation. + * + * It extends EfxExpressionTranslatorV2 to reuse XPath expression generation and + * implements EfxRulesTranslator to provide the rules translation API. + */ +@SdkComponent(versions = {"2"}, componentType = SdkComponentType.EFX_RULES_TRANSLATOR) +public class EfxRulesTranslatorV2 extends EfxExpressionTranslatorV2 + implements EfxRulesTranslator { + + private static final Logger logger = LoggerFactory.getLogger(EfxRulesTranslatorV2.class); + + /** + * The ValidatorGenerator is used to generate validation output from the intermediate model. + */ + private final ValidatorGenerator validatorGenerator; + + /** + * List of validation stages collected during parsing. + * This is the intermediate model passed to the validator generator. + */ + private CompleteValidation completeValidation = new CompleteValidation(); + + /** + * Constructor for EfxRulesTranslatorV2. + * + * @param validatorGenerator The generator for creating validation output. + * @param symbolResolver The symbol resolver for looking up fields, nodes, and codelists. + * @param scriptGenerator The script generator for creating XPath expressions. + * @param errorListener The error listener for capturing parse errors. + */ + public EfxRulesTranslatorV2(final ValidatorGenerator validatorGenerator, + final SymbolResolver symbolResolver, final ScriptGenerator scriptGenerator, + final BaseErrorListener errorListener) { + super(symbolResolver, scriptGenerator, errorListener); + this.validatorGenerator = validatorGenerator; + } + + @Override + public Map translateRules(Path pathname, TranslatorOptions options) + throws IOException { + logger.debug("Translating EFX rules from file: {}", pathname); + CharStream input = CharStreams.fromPath(pathname); + return translateRulesFromCharStream(input, options); + } + + @Override + public Map translateRules(String rules, TranslatorOptions options) { + logger.debug("Translating EFX rules from string"); + CharStream input = CharStreams.fromString(rules); + try { + return translateRulesFromCharStream(input, options); + } catch (IOException e) { + // This should never happen when reading from a string + throw new RuntimeException("Unexpected IOException while translating rules from string", e); + } + } + + @Override + public Map translateRules(InputStream stream, TranslatorOptions options) + throws IOException { + logger.debug("Translating EFX rules from input stream"); + CharStream input = CharStreams.fromStream(stream); + return translateRulesFromCharStream(input, options); + } + + /** + * Internal method to translate EFX rules from a CharStream. + * + * @param input The CharStream containing the EFX rules. + * @param options The translator options. + * @return A map of output file paths to generated content. + * @throws IOException If an I/O error occurs during translation. + */ + private Map translateRulesFromCharStream(CharStream input, + TranslatorOptions options) throws IOException { + logger.debug("Parsing EFX rules"); + + // New in EFX-2: rules preprocessing + final RulesPreprocessor preprocessor = this.new RulesPreprocessor(input); + final String preprocessedRules = preprocessor.processRules(); + + // Now parse the preprocessed rules + EfxLexer lexer = new EfxLexer(CharStreams.fromString(preprocessedRules)); + lexer.removeErrorListeners(); + lexer.addErrorListener(this.errorListener); + + CommonTokenStream tokens = new CommonTokenStream(lexer); + + EfxParser parser = new EfxParser(tokens); + parser.removeErrorListeners(); + parser.addErrorListener(this.errorListener); + + // Parse the rules file + ParseTree tree = parser.rulesFile(); + + logger.debug("Walking parse tree to build intermediate model"); + + // Initialize the stages list for this translation + this.completeValidation = new CompleteValidation(); + + // Walk the parse tree - this translator IS the listener + ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk(this, tree); + + // Generate output using the validator generator + return this.validatorGenerator.generateOutput(this.completeValidation); + } + + // #region ANTLR Listener Methods for EFX Rules Grammar + + // #region Variable Initializers + + /** + * Helper method to handle variable initializers. + * Pops the expression, creates a Variable, and pushes it back. + * This allows the variable to be declared in the stack for later reference. + */ + private void exitVariableInitializer(String variableName, + Class expressionType) { + var expression = this.stack.pop(expressionType); + var variable = new Variable(variableName, + this.script.composeVariableDeclaration(variableName, expression.getClass()), + expression, + this.script.composeVariableReference(variableName, expression.getClass())); + this.stack.push(variable); + } + + @Override + public void exitStringVariableInitializer( + EfxParser.StringVariableInitializerContext ctx) { + this.exitVariableInitializer(ctx.variableName.getText(), StringExpression.class); + } + + @Override + public void exitBooleanVariableInitializer( + EfxParser.BooleanVariableInitializerContext ctx) { + this.exitVariableInitializer(ctx.variableName.getText(), BooleanExpression.class); + } + + @Override + public void exitNumericVariableInitializer( + EfxParser.NumericVariableInitializerContext ctx) { + this.exitVariableInitializer(ctx.variableName.getText(), NumericExpression.class); + } + + @Override + public void exitDateVariableInitializer( + EfxParser.DateVariableInitializerContext ctx) { + this.exitVariableInitializer(ctx.variableName.getText(), DateExpression.class); + } + + @Override + public void exitTimeVariableInitializer( + EfxParser.TimeVariableInitializerContext ctx) { + this.exitVariableInitializer(ctx.variableName.getText(), TimeExpression.class); + } + + @Override + public void exitDurationVariableInitializer( + EfxParser.DurationVariableInitializerContext ctx) { + this.exitVariableInitializer(ctx.variableName.getText(), DurationExpression.class); + } + + // #endregion Variable Initializers + + // #region Schema-level Variables + + /** + * Called when exiting a schema-level variable declaration. + * Declares the variable as a global identifier so it can be referenced later. + * The variable will be passed to the validator generator for output generation. + */ + @Override + public void exitGlobalVariableDeclaration(GlobalVariableDeclarationContext ctx) { + logger.debug("Processing schema-level variable declaration"); + + // The variable initializer exit methods above should have created a Variable + // object and pushed it onto the stack. Pop it and declare it as global. + if (!this.stack.empty()) { + Variable variable = this.stack.pop(Variable.class); + + // Declare the variable in the stack so it can be referenced later + // and retrieved via stack.getGlobals() for the validator generator + this.stack.declareGlobalIdentifier(variable); + this.completeValidation.addGlobalVariable(variable); + logger.debug("Declared global variable: {}", variable.name); + } + } + + /** + * Called when entering a stage section. + * Each stage section becomes a validation stage in the intermediate model. + * Pushes a new ValidationStage to the stack and creates a stack frame for scoping. + */ + @Override + public void enterValidationStage(ValidationStageContext ctx) { + + this.stack.pushStackFrame(); + this.stack.push(new ValidationStage(ctx.StageIdentifier().getText())); + } + + /** + * Called when exiting a stage section. + * Pops the ValidationStage from the stack and adds it to the stages list. + */ + @Override + public void exitValidationStage(ValidationStageContext ctx) { + logger.debug("Exiting STAGE section"); + + var stage = this.stack.pop(ValidationStage.class); + this.completeValidation.addStage(stage); + this.stack.popStackFrame(); + } + + /** + * Called when exiting a pattern-level variable declaration. + * Pattern-level variables are added to the current ValidationStage. + * Uses peek to access the current stage on the stack. + */ + @Override + public void exitStageVariableDeclaration(StageVariableDeclarationContext ctx) { + logger.debug("Processing pattern-level variable declaration"); + + // The variable initializer should have created a Variable object + if (!this.stack.empty()) { + Variable variable = this.stack.pop(Variable.class); + + // Declare the variable in the stack so it can be referenced within this stage + this.stack.declareIdentifier(variable); + + this.stack.peek(ValidationStage.class).addVariable(variable); + } + } + + /** + * Called when entering a rule block. + * Pushes placeholders for rule components in the correct order. + * These will be filled in by exit methods and popped in exitRuleBlock. + */ + @Override + public void enterRuleSet(RuleSetContext ctx) { + this.stack.pushStackFrame(); + this.stack.push(new RuleSet()); + } + + /** + * Called when exiting a rule block. + * Pops all rule components from the stack, assembles them into a RuleSet, + * and adds the rule set to the current stage. + */ + @Override + public void exitRuleSet(RuleSetContext ctx) { + logger.debug("Exiting rule block"); + + var ruleSet = this.stack.pop(RuleSet.class); + this.stack.popStackFrame(); + var stage = this.stack.peek(ValidationStage.class); + stage.addRuleSet(ruleSet); + assert !this.efxContext.isEmpty() : "Expected context to be set in rule block (WITH clause should have pushed it)"; + this.efxContext.pop(); + } + + @Override + public void exitVariableInitializer(VariableInitializerContext ctx) { + var variable = this.stack.pop(Variable.class); + this.stack.declareIdentifier(variable); + this.stack.peek(RuleSet.class).addVariable(variable); + } + + @Override + public void exitContextDeclaration(ContextDeclarationContext ctx) { + String shortcut = ctx.shortcut != null ? ctx.shortcut.getText() : "none"; + switch (shortcut) { + case ".": + throw new UnsupportedOperationException("Same context (.) is not supported in EFX Rules."); + case "..": + throw new UnsupportedOperationException("Parent context (..) is not supported in EFX Rules."); + case "/": + this.exitRootContextDeclaration(); + break; + default: + PathExpression contextPath = this.stack.pop(PathExpression.class); + if (ctx.fieldContext() != null) { + String fieldId = getFieldId(ctx.fieldContext()); + assert fieldId != null : "We should have been able to locate the FieldId declared as context."; + this.exitFieldContextDeclaration(fieldId, contextPath, null); + } else if (ctx.contextVariableInitializer() != null) { + Variable contextVariable = this.getContextVariable(ctx.contextVariableInitializer(), contextPath); + assert contextVariable != null : "We should have been able to locate the ContextVariable declared as context."; + if (ctx.contextVariableInitializer().fieldContext() != null) { + String fieldId = getFieldId(ctx.contextVariableInitializer().fieldContext()); + this.exitFieldContextDeclaration(fieldId, contextPath, contextVariable); + } else if (ctx.contextVariableInitializer().nodeContext() != null) { + String nodeId = getNodeId(ctx.contextVariableInitializer().nodeContext()); + assert nodeId != null : "We should have been able to locate the NodeId declared as context."; + this.exitNodeContextDeclaration(nodeId, contextPath, contextVariable); + } + } else if (ctx.nodeContext() != null) { + String nodeId = getNodeId(ctx.nodeContext()); + assert nodeId != null : "We should have been able to locate the NodeId declared as context."; + this.exitNodeContextDeclaration(nodeId, contextPath, null); + } + break; + } + } + + private void exitRootContextDeclaration() { + PathExpression contextPath = new NodePathExpression("/*"); + String symbol = "ND-Root"; + this.exitNodeContextDeclaration(symbol, contextPath, null); + } + + private void exitFieldContextDeclaration(String fieldId, PathExpression contextPath, Variable contextVariable) { + var context = new FieldContext(fieldId, contextPath, contextVariable); + this.stack.peek(RuleSet.class).setContext(context, contextVariable); + this.efxContext.push(context); + if (contextVariable != null) { + this.stack.declareIdentifier(contextVariable); + this.efxContext.declareContextVariable(contextVariable.name, context); + } + } + + private void exitNodeContextDeclaration(String nodeId, PathExpression contextPath, Variable contextVariable) { + var context = new NodeContext(nodeId, contextPath, contextVariable); + this.stack.peek(RuleSet.class).setContext(context, contextVariable); + this.efxContext.push(context); + if (contextVariable != null) { + this.stack.declareIdentifier(contextVariable); + this.efxContext.declareContextVariable(contextVariable.name, context); + } + } + + private Variable getContextVariable(ContextVariableInitializerContext ctx, + PathExpression contextPath) { + if (ctx == null) { + return null; + } + + final String variableName = ctx.variableName.getText(); + final Class variableType = contextPath.getClass(); + + return new Variable(variableName, + this.script.composeVariableDeclaration(variableName, variableType), + this.symbols.getRelativePath(contextPath, contextPath), + this.script.composeVariableReference(variableName, variableType)); + + } + + + /** + * Called when exiting a WITH clause. + * The WITH clause defines the context and local variables for the rule. + * Pushes a RuleContextInfo object to the stack containing all WITH clause data. + */ + @Override + public void exitWithClause(WithClauseContext ctx) { + logger.debug("Processing WITH clause"); + } + + + /** + * Called when exiting a WHEN clause. + * The WHEN clause provides conditional application of the rule. + * Pushes the condition XPath string to the stack. + */ + @Override + public void exitWhenClause(WhenClauseContext ctx) { + var condition = this.stack.pop(BooleanExpression.class); + var rule = this.stack.pop(ValidationRule.class); + var invertedCondition = this.script.composeLogicalNot(condition); + rule.setCondition(condition, invertedCondition, this.combineWithOrParenthesized(invertedCondition, rule.getExpression())); + this.stack.push(rule); + } + + @Override + public void exitAsClause(AsClauseContext ctx) { + var rule = this.stack.pop(ValidationRule.class); + rule.setSeverity(RuleSeverity.fromString(ctx.severity().getText())); + rule.setId(ctx.ruleId().getText().replaceAll("^\"|\"$", "")); + this.stack.push(rule); + } + + /** + * Called when exiting a FOR clause. + * The FOR clause specifies the subject (field or node) that the rule validates. + * Pushes the subject Context to the stack. + */ + @Override + public void exitForClause(ForClauseContext ctx) { + this.stack.pop(PathExpression.class); // Remove path expression pushed by base class + + if (ctx.simpleFieldReference() != null) { + var fieldId = ctx.simpleFieldReference().FieldId().getText(); + this.stack.peek(ValidationRule.class).setSubject(new FieldContext(fieldId, this.symbols.getAbsolutePathOfField(fieldId))); + } else if (ctx.simpleNodeReference() != null) { + var nodeId = ctx.simpleNodeReference().NodeId().getText(); + this.stack.peek(ValidationRule.class).setSubject(new NodeContext(nodeId, this.symbols.getAbsolutePathOfNode(nodeId))); + } else { + assert false : "The grammar should prevent reaching this point without a field or node reference"; + } + } + + /** + * Called when exiting an IN clause. + * The IN clause specifies which notice types the assertion applies to. + */ + @Override + public void exitInClause(InClauseContext ctx) { + logger.debug("Processing IN clause"); + + String compressedList = ctx.noticeTypeList() instanceof AnyNoticeTypesContext ? "*" : ctx.noticeTypeList().getText(); + var noticeSubtypes = new NoticeSubtypeRange(compressedList, this.symbols.getAllNoticeSubtypeIds()); + this.stack.peek(ValidationRule.class).setNoticeSubtypes(noticeSubtypes); + this.completeValidation.addNoticeSubtypes(noticeSubtypes.asList()); + } + + /** + * Called when exiting an ASSERT clause. + * Build the ValidationRule and wrap it in an AssertRule. + */ + @Override + public void exitAssertClause(AssertClauseContext ctx) { + var test = this.stack.pop(BooleanExpression.class); + var rule = this.stack.pop(ValidationRule.class); + var invertedCondition = rule.getInvertedCondition(); + rule.setExpression(test, invertedCondition == null ? test : this.combineWithOrParenthesized(invertedCondition, test)); + this.stack.push(new AssertRule(rule)); + } + + @Override + public void exitReportClause(ReportClauseContext ctx) { + var test = this.stack.pop(BooleanExpression.class); + var rule = this.stack.pop(ValidationRule.class); + var invertedCondition = rule.getInvertedCondition(); + rule.setExpression(test, invertedCondition == null ? test : this.combineWithOrParenthesized(invertedCondition, test)); + this.stack.push(new ReportRule(rule)); + } + + /** + * Called when exiting an OTHERWISE clause. + * Build the ValidationRule and wrap it in an AssertRule. + */ + @Override + public void exitOtherwiseAssertClause(OtherwiseAssertClauseContext ctx) { + var test = this.stack.pop(BooleanExpression.class); + var rule = this.stack.pop(ValidationRule.class); + var invertedCondition = rule.getInvertedCondition(); + rule.setExpression(test, invertedCondition == null ? test : this.combineWithOrParenthesized(invertedCondition, test)); + this.stack.push(new AssertRule(rule)); + } + + @Override + public void exitOtherwiseReportClause(OtherwiseReportClauseContext ctx) { + var test = this.stack.pop(BooleanExpression.class); + var rule = this.stack.pop(ValidationRule.class); + var invertedCondition = rule.getInvertedCondition(); + rule.setExpression(test, invertedCondition == null ? test : this.combineWithOrParenthesized(invertedCondition, test)); + this.stack.push(new ReportRule(rule)); + } + + + private BooleanExpression combineWithOrParenthesized(BooleanExpression left, BooleanExpression right) { + if (left == null) { + return right; + } + if (right == null) { + return left; + } + return this.script.composeLogicalOr( + this.script.composeParenthesizedExpression(left, BooleanExpression.class), + this.script.composeParenthesizedExpression(right, BooleanExpression.class)); + } + + @Override + public void enterSimpleRule(SimpleRuleContext ctx) { + this.stack.push(new ValidationRule()); + } + + @Override + public void exitSimpleRule(SimpleRuleContext ctx) { + var rule = this.stack.pop(ValidationRule.class); + this.stack.peek(RuleSet.class).addRule(rule); + } + + @Override + public void enterConditionalRule(ConditionalRuleContext ctx) { + this.stack.push(new ValidationRule()); + } + + @Override + public void exitConditionalRule(ConditionalRuleContext ctx) { + var rule = this.stack.pop(ValidationRule.class); + this.stack.peek(RuleSet.class).addRule(rule); + } + + @Override + public void enterFallbackRule(FallbackRuleContext ctx) { + this.stack.push(new ValidationRule()); + } + + @Override + public void exitFallbackRule(FallbackRuleContext ctx) { + var fallbackRule = this.stack.pop(ValidationRule.class); + var ruleSet = this.stack.pop(RuleSet.class); + + BooleanExpression combined = null; + for (ValidationRule rule : ruleSet) { + assert rule != null && rule.getCondition() != null : "The EFX grammar should have prevented this. All rules in a RuleSet should have conditions if a fallback is defined."; + combined = (combined == null) ? rule.getInvertedCondition() : this.script.composeLogicalAnd(combined, rule.getInvertedCondition()); + } + var invertedCombined = this.script.composeLogicalNot(combined); + + fallbackRule.setCondition(combined, invertedCombined, this.combineWithOrParenthesized(invertedCombined, fallbackRule.getExpression())); + + ruleSet.setFallbackRule(fallbackRule); + this.stack.push(ruleSet); + } + + // #endregion ANTLR Listener Methods + + // #region RulesPreprocessor - Inner class for handling late-bound expressions + + /** + * Preprocessor for EFX Rules. + * Extends ExpressionPreprocessor to inherit common late-bound expression handling + * and adds Rules-specific variable declaration processing. + */ + class RulesPreprocessor extends ExpressionPreprocessor { + + RulesPreprocessor(final CharStream charStream) { + super(charStream); + } + + String processRules() { + final ParseTree tree = parser.rulesFile(); + final ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk(this, tree); + return this.rewriter.getText(); + } + + // #region Rules-specific variable declarations + + /** + * Process schema-level variable declarations in the preprocessor. + * This allows variable types to be known for later reference. + */ + @Override + public void exitGlobalVariableDeclaration(GlobalVariableDeclarationContext ctx) { + // The variable initializer should have created a Variable and pushed it + if (!this.stack.empty()) { + Variable variable = this.stack.pop(Variable.class); + // Declare it in the preprocessor's stack so it can be referenced + this.stack.declareGlobalIdentifier(variable); + } + } + + /** + * Process pattern-level variable declarations in the preprocessor. + */ + @Override + public void exitStageVariableDeclaration( + EfxParser.StageVariableDeclarationContext ctx) { + if (!this.stack.empty()) { + Variable variable = this.stack.pop(Variable.class); + this.stack.declareIdentifier(variable); + } + } + + /** + * Push a new stack frame when entering a validation stage. + * This ensures stage-level variables are scoped to their stage. + */ + @Override + public void enterValidationStage(ValidationStageContext ctx) { + this.stack.pushStackFrame(); + } + + /** + * Pop the stack frame when exiting a validation stage. + */ + @Override + public void exitValidationStage(ValidationStageContext ctx) { + this.stack.popStackFrame(); + } + + @Override + public void exitContextDeclaration(ContextDeclarationContext ctx) { + final var initializer = ctx.contextVariableInitializer(); + if (initializer == null) { + return; // No context variable initializer, nothing to do during pre-processing. + } + if (initializer.fieldContext() != null) { + final String fieldId = getFieldId(initializer.fieldContext()); + var fieldType = FieldTypes.fromString(this.symbols.getTypeOfField(fieldId)); + this.stack.declareIdentifier( + new Variable(initializer.variableName.getText(), PathExpression.instantiate("", fieldType), + PathExpression.instantiate("", fieldType), PathExpression.instantiate("", fieldType))); + } + if (initializer.nodeContext() != null) { + this.stack.declareIdentifier(new Variable(initializer.variableName.getText(), NodePathExpression.empty(), + NodePathExpression.empty(), NodePathExpression.empty())); + } + return; + } + + /** + * Process template-level variable declarations in the preprocessor (WITH clause). + */ + @Override + public void exitVariableInitializer( + EfxParser.VariableInitializerContext ctx) { + if (!this.stack.empty()) { + Variable variable = this.stack.pop(Variable.class); + this.stack.declareIdentifier(variable); + } + } + + // #endregion Rules-specific variable declarations + + // #region Variable initializers for type tracking + + /** + * Variable initializer exit methods for the preprocessor. + * These create simple Variable objects with empty expressions just for type tracking. + */ + @Override + public void exitStringVariableInitializer(EfxParser.StringVariableInitializerContext ctx) { + this.stack.push(new Variable(ctx.variableName.getText(), + StringExpression.empty(), StringExpression.empty())); + } + + @Override + public void exitBooleanVariableInitializer(EfxParser.BooleanVariableInitializerContext ctx) { + this.stack.push(new Variable(ctx.variableName.getText(), + BooleanExpression.empty(), BooleanExpression.empty())); + } + + @Override + public void exitNumericVariableInitializer(EfxParser.NumericVariableInitializerContext ctx) { + this.stack.push(new Variable(ctx.variableName.getText(), + NumericExpression.empty(), NumericExpression.empty())); + } + + @Override + public void exitDateVariableInitializer(EfxParser.DateVariableInitializerContext ctx) { + this.stack.push(new Variable(ctx.variableName.getText(), + DateExpression.empty(), DateExpression.empty())); + } + + @Override + public void exitTimeVariableInitializer(EfxParser.TimeVariableInitializerContext ctx) { + this.stack.push(new Variable(ctx.variableName.getText(), + TimeExpression.empty(), TimeExpression.empty())); + } + + @Override + public void exitDurationVariableInitializer(EfxParser.DurationVariableInitializerContext ctx) { + this.stack.push(new Variable(ctx.variableName.getText(), + DurationExpression.empty(), DurationExpression.empty())); + } + + // #endregion Variable initializers for type tracking + } + + // #endregion RulesPreprocessor +} diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java index ed0cdf57..e6b2850f 100644 --- a/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java +++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java @@ -92,7 +92,7 @@ import eu.europa.ted.efx.sdk2.EfxParser.DurationParameterDeclarationContext; import eu.europa.ted.efx.sdk2.EfxParser.DurationVariableInitializerContext; import eu.europa.ted.efx.sdk2.EfxParser.ExpressionTemplateContext; -import eu.europa.ted.efx.sdk2.EfxParser.GlobalVariableDeclarationContext; +import eu.europa.ted.efx.sdk2.EfxParser.VariableDeclarationContext; import eu.europa.ted.efx.sdk2.EfxParser.IndentationContext; import eu.europa.ted.efx.sdk2.EfxParser.InvokeTemplateContext; import eu.europa.ted.efx.sdk2.EfxParser.LabelTemplateContext; @@ -124,7 +124,7 @@ import eu.europa.ted.efx.sdk2.EfxParser.TemplateDeclarationContext; import eu.europa.ted.efx.sdk2.EfxParser.TemplateFileContext; import eu.europa.ted.efx.sdk2.EfxParser.TemplateLineContext; -import eu.europa.ted.efx.sdk2.EfxParser.TemplateVariableDeclarationContext; +import eu.europa.ted.efx.sdk2.EfxParser.VariableInitializerContext; import eu.europa.ted.efx.sdk2.EfxParser.TextTemplateContext; import eu.europa.ted.efx.sdk2.EfxParser.TimeFunctionDeclarationContext; import eu.europa.ted.efx.sdk2.EfxParser.TimeParameterDeclarationContext; @@ -323,7 +323,7 @@ private void generateAndSaveProfilerReport(final EfxParser parser, final Path pr // #region Global declarationExpressions --------------------------------------- @Override - public void exitGlobalVariableDeclaration(GlobalVariableDeclarationContext ctx) { + public void exitVariableDeclaration(VariableDeclarationContext ctx) { var variable = this.stack.pop(Variable.class); this.stack.declareGlobalIdentifier(variable); } @@ -937,18 +937,20 @@ private void exitRootContextDeclaration() { } private void exitFieldContextDeclaration(String fieldId, PathExpression contextPath, Variable contextVariable) { - this.efxContext.push(new FieldContext(fieldId, contextPath, contextVariable)); + var context = new FieldContext(fieldId, contextPath, contextVariable); + this.efxContext.push(context); if (contextVariable != null) { this.stack.declareIdentifier(contextVariable); - this.efxContext.declareContextVariable(contextVariable.name, new FieldContext(fieldId, contextPath, contextVariable)); + this.efxContext.declareContextVariable(contextVariable.name, context); } } private void exitNodeContextDeclaration(String nodeId, PathExpression contextPath, Variable contextVariable) { - this.efxContext.push(new NodeContext(nodeId, contextPath, contextVariable)); + var context = new NodeContext(nodeId, contextPath, contextVariable); + this.efxContext.push(context); if (contextVariable != null) { this.stack.declareIdentifier(contextVariable); - this.efxContext.declareContextVariable(contextVariable.name, new NodeContext(nodeId, contextPath, contextVariable)); + this.efxContext.declareContextVariable(contextVariable.name, context); } } @@ -1051,7 +1053,7 @@ private void exitVariableInitializer( } @Override - public void exitTemplateVariableDeclaration(TemplateVariableDeclarationContext arg0) { + public void exitVariableInitializer(VariableInitializerContext arg0) { var variable = this.stack.pop(Variable.class); this.stack.declareIdentifier(variable); this.stack.peek(Variables.class).add(variable); @@ -1344,13 +1346,13 @@ String processTemplate() { // #region Template Variables --------------------------------------------- @Override - public void exitGlobalVariableDeclaration(GlobalVariableDeclarationContext arg0) { + public void exitVariableDeclaration(VariableDeclarationContext arg0) { var variable = this.stack.pop(Variable.class); this.stack.declareGlobalIdentifier(variable); } @Override - public void exitTemplateVariableDeclaration(TemplateVariableDeclarationContext arg0) { + public void exitVariableInitializer(VariableInitializerContext arg0) { var variable = this.stack.pop(Variable.class); this.stack.declareIdentifier(variable); } diff --git a/src/main/java/eu/europa/ted/efx/sdk2/entity/SdkNoticeSubtypeV2.java b/src/main/java/eu/europa/ted/efx/sdk2/entity/SdkNoticeSubtypeV2.java new file mode 100644 index 00000000..337d543d --- /dev/null +++ b/src/main/java/eu/europa/ted/efx/sdk2/entity/SdkNoticeSubtypeV2.java @@ -0,0 +1,21 @@ +package eu.europa.ted.efx.sdk2.entity; + +import com.fasterxml.jackson.databind.JsonNode; +import eu.europa.ted.eforms.sdk.component.SdkComponent; +import eu.europa.ted.eforms.sdk.component.SdkComponentType; +import eu.europa.ted.efx.sdk1.entity.SdkNoticeSubtypeV1; + +/** + * Represents a notice subtype from the SDK's notice-types.json file. + */ +@SdkComponent(versions = {"2"}, componentType = SdkComponentType.NOTICE_TYPE) +public class SdkNoticeSubtypeV2 extends SdkNoticeSubtypeV1 { + + public SdkNoticeSubtypeV2(String subTypeId, String documentType, String type) { + super(subTypeId, documentType, type); + } + + public SdkNoticeSubtypeV2(JsonNode json) { + super(json); + } +} diff --git a/src/main/resources/freemarker/schematron/complete-validation.ftl b/src/main/resources/freemarker/schematron/complete-validation.ftl new file mode 100644 index 00000000..b6057a99 --- /dev/null +++ b/src/main/resources/freemarker/schematron/complete-validation.ftl @@ -0,0 +1,57 @@ + +<#-- + Template for complete-validation.sch master file. + + Parameters: + title - Schema title (e.g., "eForms validation (dynamic)") + globalVariables - List of schema-level variables + phases - List defining validation phases per notice type + diagnostics - List for subject path information + includes - List of pattern file paths to include +--> + + + ${title} + + <#-- Namespace declarations - hardcoded as per design decision --> + + + + + + + + + + + + + + <#-- Global variables from schema-level LET statements --> +<#list globalVariables as variable> + + + + <#-- Phases for each notice type --> +<#list phases as phase> + + <#list phase.activePatterns as pattern> + + + + +<#if diagnostics?has_content> + + + <#list diagnostics as diagnostic> + ${diagnostic.xpath} + + + + + <#-- Includes for all pattern files --> +<#list includes as include> + + + + diff --git a/src/main/resources/freemarker/schematron/pattern.ftl b/src/main/resources/freemarker/schematron/pattern.ftl new file mode 100644 index 00000000..dd2e89a6 --- /dev/null +++ b/src/main/resources/freemarker/schematron/pattern.ftl @@ -0,0 +1,29 @@ + +<#-- + Template for individual Schematron pattern file. + + Parameters: + id - Pattern identifier (e.g., "validation-stage-1a-1") + variables - List of pattern-level variables + rules - List containing the validation rules + tags - List of tags specifying which tests to include +--> + +<#list variables as variable> + + +<#list rules as rule> +<#if rule.hasTestsForTags(tags)> + + <#list rule.variables as variable> + + + <#list rule.tests as test> + <#if tags?seq_contains(test.tag)> + <${test.elementName} id="${test.id}" role="${test.role}"<#if test.diagnostic??> diagnostics="${test.diagnostic.id}" test="${test.test?xml?replace("'", "'")}">${test.message} + + + + + + diff --git a/src/test/java/eu/europa/ted/efx/mock/DependencyFactoryMock.java b/src/test/java/eu/europa/ted/efx/mock/DependencyFactoryMock.java index 65070d76..13549ac1 100644 --- a/src/test/java/eu/europa/ted/efx/mock/DependencyFactoryMock.java +++ b/src/test/java/eu/europa/ted/efx/mock/DependencyFactoryMock.java @@ -12,19 +12,21 @@ import eu.europa.ted.efx.interfaces.SymbolResolver; import eu.europa.ted.efx.interfaces.TranslatorDependencyFactory; import eu.europa.ted.efx.interfaces.TranslatorOptions; +import eu.europa.ted.efx.interfaces.ValidatorGenerator; // Import /** * Provides EfxTranslator dependencies used for unit testing. */ public class DependencyFactoryMock implements TranslatorDependencyFactory { - private DependencyFactoryMock() {} + private DependencyFactoryMock() { + } final public static DependencyFactoryMock INSTANCE = new DependencyFactoryMock(); Map scriptGenerators = new HashMap<>(); Map markupGenerators = new HashMap<>(); - + @Override public SymbolResolver createSymbolResolver(String sdkVersion, String qualifier) { // Ignore the qualifier for unit tests @@ -52,6 +54,13 @@ public MarkupGenerator createMarkupGenerator(String sdkVersion, String qualifier return this.markupGenerators.computeIfAbsent(key, k -> new MarkupGeneratorMock()); } + @Override + public ValidatorGenerator createValidatorGenerator(String sdkVersion, String qualifier, + TranslatorOptions options) { + // Return null or a mock implementation as needed for tests + return null; + } + @Override public BaseErrorListener createErrorListener() { return ThrowingErrorListener.INSTANCE; diff --git a/src/test/java/eu/europa/ted/efx/mock/sdk1/SymbolResolverMockV1.java b/src/test/java/eu/europa/ted/efx/mock/sdk1/SymbolResolverMockV1.java index f515a438..c0408de9 100644 --- a/src/test/java/eu/europa/ted/efx/mock/sdk1/SymbolResolverMockV1.java +++ b/src/test/java/eu/europa/ted/efx/mock/sdk1/SymbolResolverMockV1.java @@ -87,4 +87,16 @@ public String getNodeIdFromAlias(String alias) { throw new UnsupportedOperationException( "Alias resolution is not supported in SDK-1."); } + + /** + * Returns a list of all valid notice type IDs for testing. + * Uses a small set that covers all testing scenarios: + * - Numeric types (1-5) for basic testing and ranges + * - Alphanumeric types (E1, E2, X01) for non-numeric ID testing + */ + @Override + public java.util.List getAllNoticeSubtypeIds() { + return java.util.Arrays.asList("1", "2", "3", "4", "5", "E1", "E2", "X01"); + } + } diff --git a/src/test/java/eu/europa/ted/efx/mock/sdk2/SymbolResolverMockV2.java b/src/test/java/eu/europa/ted/efx/mock/sdk2/SymbolResolverMockV2.java index a92913b0..3bf2860a 100644 --- a/src/test/java/eu/europa/ted/efx/mock/sdk2/SymbolResolverMockV2.java +++ b/src/test/java/eu/europa/ted/efx/mock/sdk2/SymbolResolverMockV2.java @@ -118,4 +118,15 @@ public String getNodeIdFromAlias(String alias) { } return null; } + + /** + * Returns a list of all valid notice type IDs for testing. + * Uses a small set that covers all testing scenarios: + * - Numeric types (1-5) for basic testing and ranges + * - Alphanumeric types (E1, E2, X01) for non-numeric ID testing + */ + @Override + public java.util.List getAllNoticeSubtypeIds() { + return java.util.Arrays.asList("1", "2", "3", "4", "5", "E1", "E2", "X01"); + } } diff --git a/src/test/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test.java b/src/test/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test.java new file mode 100644 index 00000000..cce1dc6d --- /dev/null +++ b/src/test/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test.java @@ -0,0 +1,442 @@ +/* + * Copyright 2022 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 + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.efx.sdk2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.xml.sax.InputSource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import eu.europa.ted.efx.EfxTestsBase; +import eu.europa.ted.efx.EfxTranslatorOptions; +import eu.europa.ted.efx.exceptions.ThrowingErrorListener; +import eu.europa.ted.efx.mock.DependencyFactoryMock; +import eu.europa.ted.efx.model.DecimalFormat; +import eu.europa.ted.eforms.sdk.schematron.SchematronGenerator; + +/** + * Unit tests for EfxRulesTranslatorV2. + * Tests the transpilation of EFX Rules files to Schematron XML. + */ +class EfxRulesTranslatorV2Test extends EfxTestsBase { + + private static final String SDK_VERSION = "eforms-sdk-2.0"; + private EfxRulesTranslatorV2 translator; + + @Override + protected String getSdkVersion() { + return SDK_VERSION; + } + + @BeforeEach + void setUp() throws InstantiationException { + translator = new EfxRulesTranslatorV2( + new SchematronGenerator(), + DependencyFactoryMock.INSTANCE.createSymbolResolver(SDK_VERSION, ""), + DependencyFactoryMock.INSTANCE.createScriptGenerator(SDK_VERSION, "", + new EfxTranslatorOptions("udf", DecimalFormat.EFX_DEFAULT)), + ThrowingErrorListener.INSTANCE); + } + + //#region Private helper methods + + private String readExpected(String testMethodName, String filename) throws IOException { + String resourcePath = "/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/" + + testMethodName + "/" + filename; + try (InputStream stream = getClass().getResourceAsStream(resourcePath)) { + if (stream == null) { + throw new IOException("Resource not found: " + resourcePath); + } + return new String(stream.readAllBytes(), StandardCharsets.UTF_8); + } + } + + private String readInput(String testMethodName) throws IOException { + return readExpected(testMethodName, "input.efx"); + } + + private void assertValidXml(String xmlContent, String description) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + builder.parse(new InputSource(new StringReader(xmlContent))); + } catch (Exception e) { + throw new AssertionError(description + " is not valid XML: " + e.getMessage(), e); + } + } + + /** + * Verifies all generated output files match expected content. + * Reads expected files from test resources folder structure that mirrors actual output. + */ + private void assertAllOutputs(String testName, Map outputFiles) throws IOException { + for (Map.Entry entry : outputFiles.entrySet()) { + String filename = entry.getKey(); + String actualContent = entry.getValue(); + + String expectedContent = readExpected(testName, filename); + + if (filename.endsWith(".sch")) { + assertValidXml(actualContent, filename); + } + assertEquals(expectedContent.stripTrailing(), actualContent.stripTrailing(), + "Content mismatch in " + filename); + } + } + + //#endregion Private helper methods + + //#region STAGE tests + + @Test + void testStage_Multiple_GeneratesSeparatePatterns() throws IOException { + String testName = "testStage_Multiple_GeneratesSeparatePatterns"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(11, outputFiles.size(), "Should generate exactly 11 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion STAGE tests + + //#region WITH clause tests + + @Test + void testWithClause_RootContextShortcut() throws IOException { + String testName = "testWithClause_RootContextShortcut"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + @Test + void testWithClause_ContextVariable() throws IOException { + String testName = "testWithClause_ContextVariable"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + @Test + void testWithClause_VariablePositioning() throws IOException { + String testName = "testWithClause_VariablePositioning"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(11, outputFiles.size(), "Should generate 11 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion WITH clause tests + + //#region ASSERT and REPORT tests + + @Test + void testAssertAndReport_Simple() throws IOException { + String testName = "testAssertAndReport_Simple"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(5, outputFiles.size(), "Should generate exactly 5 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion ASSERT and REPORT tests + + //#region WHEN/OTHERWISE tests + + @Test + void testWhen_SimpleCondition() throws IOException { + String testName = "testWhen_SimpleCondition"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + @Test + void testWhen_WithOtherwise() throws IOException { + String testName = "testWhen_WithOtherwise"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion WHEN/OTHERWISE tests + + //#region FOR clause tests + + @Test + void testForClause_NodeReference() throws IOException { + String testName = "testForClause_NodeReference"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion FOR clause tests + + //#region IN clause tests + + @Test + void testInClause_SpecificNoticeTypes() throws IOException { + String testName = "testInClause_SpecificNoticeTypes"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(9, outputFiles.size(), "Should generate exactly 9 files"); + assertAllOutputs(testName, outputFiles); + } + + @Test + void testInClause_NoticeTypeRange() throws IOException { + String testName = "testInClause_NoticeTypeRange"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(15, outputFiles.size(), "Should generate exactly 15 files"); + assertAllOutputs(testName, outputFiles); + } + + @Test + void testInClause_AllNoticeTypes() throws IOException { + String testName = "testInClause_AllNoticeTypes"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(19, outputFiles.size(), "Should generate 19 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion IN clause tests + + //#region Variable tests (output verification) + + @Test + void testVariable_Global_AppearsBeforeIncludes() throws IOException { + String testName = "testVariable_Global_AppearsBeforeIncludes"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + + // Verify global variables appear before elements in complete-validation.sch + String completeValidation = outputFiles.get("dynamic/complete-validation.sch"); + int letIndex = completeValidation.indexOf(" elements"); + } + + @Test + void testVariable_StageLevel() throws IOException { + String testName = "testVariable_StageLevel"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + + // Verify each pattern has its own variable with the correct value + String stage1a = outputFiles.get("dynamic/validation-stage-1a-1.sch"); + String stage1b = outputFiles.get("dynamic/validation-stage-1b-1.sch"); + + assertTrue(stage1a.contains("name=\"stageVar\""), "Stage 1a should have stageVar variable"); + assertTrue(stage1a.contains(""first""), "Stage 1a stageVar should have value 'first'"); + + assertTrue(stage1b.contains("name=\"stageVar\""), "Stage 1b should have stageVar variable"); + assertTrue(stage1b.contains(""second""), "Stage 1b stageVar should have value 'second'"); + } + + //#endregion Variable tests (output verification) + + //#region Variable tests (error verification) + + @Test + void testVariable_GlobalOnly_FailsWithoutStage() { + String simpleRules = "LET text : $testVar = \"test-value\";"; + assertThrows(Exception.class, () -> translator.translateRules(simpleRules)); + } + + @Test + void testVariable_StageScopeIsolation() { + String rulesWithStageScoping = lines( + "---- STAGE 1a ----", + "LET text : $stageVar = \"stage1\";", + "", + "WITH BT-00-Text", + "ASSERT $stageVar is not empty", + "AS ERROR BR-BT-00001-0001", + "FOR BT-00-Text IN 1, 2;", + "", + "---- STAGE 1b ----", + "WITH BT-01-Text", + "ASSERT $stageVar is not empty", + "AS ERROR BR-BT-00001-0002", + "FOR BT-01-Text IN 1, 2;" + ); + + assertThrows(Exception.class, () -> translator.translateRules(rulesWithStageScoping), + "Pattern-level variables should not be visible across stages"); + } + + @Test + void testVariable_RuleScopeIsolation() { + String rulesWithRuleScoping = lines( + "---- STAGE 1a ----", + "WITH text : $ruleVar = \"rule1\", BT-00-Text", + "ASSERT $ruleVar is not empty", + "AS ERROR BR-BT-00001-0001", + "FOR BT-00-Text IN 1, 2;", + "", + "WITH BT-01-Text", + "ASSERT $ruleVar is not empty", + "AS ERROR BR-BT-00001-0002", + "FOR BT-01-Text IN 1, 2;" + ); + + assertThrows(Exception.class, () -> translator.translateRules(rulesWithRuleScoping), + "WITH clause variables should not be visible across rules"); + } + + //#endregion Variable tests (error verification) + + //#region Diagnostics tests + + @Test + void testDiagnostics_MultipleFields() throws IOException { + String testName = "testDiagnostics_MultipleFields"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + @Test + void testDiagnostics_NoDuplicateEntries() throws IOException { + String testName = "testDiagnostics_NoDuplicateEntries"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + @Test + void testDiagnostics_SanitizesParentheses() throws IOException { + String testName = "testDiagnostics_SanitizesParentheses"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(7, outputFiles.size(), "Should generate exactly 7 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion Diagnostics tests + + @Test + void testInClause_PhaseGeneration() throws IOException { + String testName = "testInClause_PhaseGeneration"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(19, outputFiles.size(), "Should generate exactly 19 files"); + assertAllOutputs(testName, outputFiles); + + // Verify phase generation logic + String completeValidation = outputFiles.get("dynamic/complete-validation.sch"); + + assertTrue(completeValidation.contains("")); + assertTrue(completeValidation.contains("")); + assertTrue(completeValidation.contains("")); + assertFalse(completeValidation.contains("")); + + String phase1 = extractPhase(completeValidation, "eforms-1"); + assertTrue(phase1.contains("validation-stage-1")); + assertTrue(phase1.contains("validation-stage-2")); + assertTrue(phase1.contains("validation-stage-3")); + + String phase2 = extractPhase(completeValidation, "eforms-2"); + assertTrue(phase2.contains("validation-stage-1")); + assertTrue(phase2.contains("validation-stage-2")); + assertFalse(phase2.contains("validation-stage-3")); + + String phase3 = extractPhase(completeValidation, "eforms-3"); + assertTrue(phase3.contains("validation-stage-1")); + assertTrue(phase3.contains("validation-stage-2")); + assertTrue(phase3.contains("validation-stage-3")); + } + + private String extractPhase(String content, String phaseId) { + int start = content.indexOf(""); + int end = content.indexOf("", start); + return content.substring(start, end); + } + + //#region Comprehensive/Integration tests + + @Test + void testOutput_FromSampleRulesFile() throws IOException { + String testName = "testOutput_FromSampleRulesFile"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertFalse(outputFiles.isEmpty(), "Output files should not be empty"); + assertEquals(57, outputFiles.size(), "Should generate exactly 57 files"); + + // Verify we have the expected files + assertTrue(outputFiles.containsKey("dynamic/complete-validation.sch")); + assertTrue(outputFiles.containsKey("static/complete-validation.sch")); + assertTrue(outputFiles.containsKey("schematrons.json")); + + // Check that patterns exist for each stage + assertTrue(outputFiles.keySet().stream().anyMatch(f -> f.startsWith("dynamic/validation-stage-1a-"))); + assertTrue(outputFiles.keySet().stream().anyMatch(f -> f.startsWith("dynamic/validation-stage-1b-"))); + assertTrue(outputFiles.keySet().stream().anyMatch(f -> f.startsWith("dynamic/validation-stage-2a-"))); + assertTrue(outputFiles.keySet().stream().anyMatch(f -> f.startsWith("dynamic/validation-stage-3a-"))); + assertTrue(outputFiles.keySet().stream().anyMatch(f -> f.startsWith("static/validation-stage-1a-"))); + + // Verify XML well-formedness for all .sch files + for (Map.Entry entry : outputFiles.entrySet()) { + if (entry.getKey().endsWith(".sch")) { + assertValidXml(entry.getValue(), entry.getKey()); + } + } + + // Verify schematrons.json structure + String schematronsJson = outputFiles.get("schematrons.json"); + assertTrue(schematronsJson.contains("\"schematrons\"")); + assertTrue(schematronsJson.contains("\"type\" : \"dynamic\"")); + assertTrue(schematronsJson.contains("\"type\" : \"static\"")); + } + + @Test + void testOutput_ComprehensiveMixedRules() throws IOException { + String testName = "testOutput_ComprehensiveMixedRules"; + Map outputFiles = translator.translateRules(readInput(testName)); + + assertEquals(11, outputFiles.size(), "Should generate 11 files"); + assertAllOutputs(testName, outputFiles); + } + + //#endregion Comprehensive/Integration tests +} diff --git a/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java b/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java index b030c4e4..6e2d4ace 100644 --- a/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java +++ b/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java @@ -1298,10 +1298,10 @@ void testChooseTemplate_WhenNoOtherwiseNoContext() { // #endregion chooseTemplate --------------------------------- - // #region templateVariableList --------------------------------------------- + // #region variableList --------------------------------------------- @Test - void testTemplateVariableList_WithAllDataTypes() { + void testVariableList_WithAllDataTypes() { assertEquals( lines( "TEMPLATES:", @@ -1313,7 +1313,7 @@ void testTemplateVariableList_WithAllDataTypes() { } @Test - void testTemplateVariableList_ExpressionInitializers() { + void testVariableList_ExpressionInitializers() { assertEquals( lines( "TEMPLATES:", @@ -1323,7 +1323,7 @@ void testTemplateVariableList_ExpressionInitializers() { translateTemplate("{BT-00-Text, text:$computed=concat('prefix-', BT-00-Text)} Computed: ${$computed}")); } - // #endregion templateVariableList ------------------------------------------ + // #endregion variableList ------------------------------------------ // #region templateLine edge cases ------------------------------------------ diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/dynamic/complete-validation.sch new file mode 100644 index 00000000..241de272 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/dynamic/complete-validation.sch @@ -0,0 +1,26 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..4fe181cd --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/input.efx new file mode 100644 index 00000000..a1090ec3 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/input.efx @@ -0,0 +1,13 @@ +// Test: Simple ASSERT and REPORT rules +// Verifies both rule types generate correct Schematron elements + +---- STAGE 1a ---- + +WITH BT-00-Text + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1 + + REPORT BT-00-Text is empty + AS WARNING R-X3F-N8W + FOR BT-00-Text IN 1 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/schematrons.json new file mode 100644 index 00000000..c8f0b9a5 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/schematrons.json @@ -0,0 +1,21 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/static/complete-validation.sch new file mode 100644 index 00000000..241de272 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/static/complete-validation.sch @@ -0,0 +1,26 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..4fe181cd --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/static/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..5f5ab1eb --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..9407d438 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/input.efx new file mode 100644 index 00000000..d1506e52 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/input.efx @@ -0,0 +1,15 @@ +// Test: Multiple WITH statements with different fields +// Verifies no diagnostics are generated when context == subject field +// (BT-00-Text context with BT-00-Text subject = no diagnostics) + +---- STAGE 1a ---- + +WITH BT-00-Text + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 + +WITH BT-00-Indicator + ASSERT BT-00-Indicator is present + AS ERROR R-D4K-P9M + FOR BT-00-Indicator IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..5f5ab1eb --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/validation-stage-1a-1.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..9407d438 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_MultipleFields/static/validation-stage-1a-2.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..bb2d3d99 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..28cf331f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/input.efx new file mode 100644 index 00000000..a442b50e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/input.efx @@ -0,0 +1,16 @@ +// Test: Same field used in multiple rules +// Verifies no duplicate diagnostics when same field referenced twice +// Both rules use BT-00-Text as context and subject + +---- STAGE 1a ---- + +WITH BT-00-Text + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 + +WITH BT-00-Text + WHEN BT-00-Text is not empty + ASSERT TRUE + AS ERROR R-D4K-P9M + FOR BT-00-Text IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..bb2d3d99 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/validation-stage-1a-1.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..28cf331f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_NoDuplicateEntries/static/validation-stage-1a-2.sch @@ -0,0 +1,9 @@ + + + + rule|text|R-K7P-M2Q + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/complete-validation.sch new file mode 100644 index 00000000..a79a80cd --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextFieldA + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..1f9f403d --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..e0b1dba7 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/input.efx new file mode 100644 index 00000000..dd03b1ae --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/input.efx @@ -0,0 +1,10 @@ +// Test: Field IDs with parentheses in diagnostics +// Verifies parentheses are sanitized: BT-00(a)-Text -> BT-00_a_-Text +// Original ID preserved in 'see' attribute, sanitized in diagnostic ID + +---- STAGE 1a ---- + +WITH ND-Root + ASSERT BT-00(a)-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00(a)-Text IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/complete-validation.sch new file mode 100644 index 00000000..a79a80cd --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextFieldA + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..1f9f403d --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..e0b1dba7 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testDiagnostics_SanitizesParentheses/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/complete-validation.sch new file mode 100644 index 00000000..7ca02aba --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + ../../SubNode + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..a4a7f695 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..de01d376 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/input.efx new file mode 100644 index 00000000..2a44a0ec --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/input.efx @@ -0,0 +1,10 @@ +// Test: FOR clause with node reference instead of field +// Verifies FOR ND-SubNode uses node path in diagnostics +// Node reference works same as field reference + +---- STAGE 1a ---- + +WITH BT-00-Text + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR ND-SubNode IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/complete-validation.sch new file mode 100644 index 00000000..7ca02aba --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + ../../SubNode + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..a4a7f695 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..de01d376 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testForClause_NodeReference/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/complete-validation.sch new file mode 100644 index 00000000..a0f64303 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/complete-validation.sch @@ -0,0 +1,54 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..08ab6e90 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..ad6fba81 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-3.sch new file mode 100644 index 00000000..514ed8d4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-3.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-4.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-4.sch new file mode 100644 index 00000000..9b55a9c8 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-4.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-5.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-5.sch new file mode 100644 index 00000000..a28a69ae --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-5.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E1.sch new file mode 100644 index 00000000..0900794e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E2.sch new file mode 100644 index 00000000..64098efb --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E2.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-X01.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-X01.sch new file mode 100644 index 00000000..9a333760 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-X01.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/input.efx new file mode 100644 index 00000000..5dcbfbb5 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/input.efx @@ -0,0 +1,13 @@ +// Test: IN * and IN ANY (all notice types) +// Verifies both syntaxes expand to all available notice types + +---- STAGE 1a ---- + +WITH BT-00-Text + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN * + + ASSERT BT-00-Text is not empty + AS WARNING R-X3F-N8W + FOR BT-00-Text IN ANY diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/schematrons.json new file mode 100644 index 00000000..64d06370 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/schematrons.json @@ -0,0 +1,91 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1a-3", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-3.sch" + }, { + "name" : "validation-stage-1a-4", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-4.sch" + }, { + "name" : "validation-stage-1a-5", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-5.sch" + }, { + "name" : "validation-stage-1a-E1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-E1.sch" + }, { + "name" : "validation-stage-1a-E2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-E2.sch" + }, { + "name" : "validation-stage-1a-X01", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-X01.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1a-3", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-3.sch" + }, { + "name" : "validation-stage-1a-4", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-4.sch" + }, { + "name" : "validation-stage-1a-5", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-5.sch" + }, { + "name" : "validation-stage-1a-E1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-E1.sch" + }, { + "name" : "validation-stage-1a-E2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-E2.sch" + }, { + "name" : "validation-stage-1a-X01", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-X01.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/complete-validation.sch new file mode 100644 index 00000000..a0f64303 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/complete-validation.sch @@ -0,0 +1,54 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..08ab6e90 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..ad6fba81 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-2.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-3.sch new file mode 100644 index 00000000..514ed8d4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-3.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-4.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-4.sch new file mode 100644 index 00000000..9b55a9c8 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-4.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-5.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-5.sch new file mode 100644 index 00000000..a28a69ae --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-5.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E1.sch new file mode 100644 index 00000000..0900794e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E2.sch new file mode 100644 index 00000000..64098efb --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E2.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-X01.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-X01.sch new file mode 100644 index 00000000..9a333760 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-X01.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/complete-validation.sch new file mode 100644 index 00000000..3d5d250d --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/complete-validation.sch @@ -0,0 +1,50 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../PathNode/TextField + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..8ef796f4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..fdac6faf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-3.sch new file mode 100644 index 00000000..0fb27c70 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-5.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-5.sch new file mode 100644 index 00000000..fe4b886f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-5.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-E1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-E1.sch new file mode 100644 index 00000000..3f9ac8a5 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-E1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-E2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-E2.sch new file mode 100644 index 00000000..5c4b7bcf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/dynamic/validation-stage-1a-E2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/input.efx new file mode 100644 index 00000000..2726c76c --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/input.efx @@ -0,0 +1,10 @@ +// Test: IN clause with notice type ranges +// Verifies range expansion: 1-3 -> (1,2,3), 5, E1-E2 -> (E1,E2) +// Total: 6 notice types generate 6 pattern files + +---- STAGE 1a ---- + +WITH ND-SubNode + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1-3, 5, E1-E2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/schematrons.json new file mode 100644 index 00000000..aba6b4e0 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/schematrons.json @@ -0,0 +1,71 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1a-3", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-3.sch" + }, { + "name" : "validation-stage-1a-5", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-5.sch" + }, { + "name" : "validation-stage-1a-E1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-E1.sch" + }, { + "name" : "validation-stage-1a-E2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-E2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1a-3", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-3.sch" + }, { + "name" : "validation-stage-1a-5", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-5.sch" + }, { + "name" : "validation-stage-1a-E1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-E1.sch" + }, { + "name" : "validation-stage-1a-E2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-E2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/complete-validation.sch new file mode 100644 index 00000000..3d5d250d --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/complete-validation.sch @@ -0,0 +1,50 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../PathNode/TextField + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..8ef796f4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..fdac6faf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-3.sch new file mode 100644 index 00000000..0fb27c70 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-5.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-5.sch new file mode 100644 index 00000000..fe4b886f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-5.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-E1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-E1.sch new file mode 100644 index 00000000..3f9ac8a5 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-E1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-E2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-E2.sch new file mode 100644 index 00000000..5c4b7bcf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_NoticeTypeRange/static/validation-stage-1a-E2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/complete-validation.sch new file mode 100644 index 00000000..6d0cec74 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/complete-validation.sch @@ -0,0 +1,44 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-1.sch new file mode 100644 index 00000000..1388ac63 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-2.sch new file mode 100644 index 00000000..8609bb6a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-3.sch new file mode 100644 index 00000000..f4743399 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-1.sch new file mode 100644 index 00000000..ae353b65 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-Y2N-G7S + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-2.sch new file mode 100644 index 00000000..b2ac866a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-3.sch new file mode 100644 index 00000000..8dd1b04e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-2-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-Y2N-G7S + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-3-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-3-1.sch new file mode 100644 index 00000000..034aab3a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-3-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-F5V-T6B + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-3-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-3-3.sch new file mode 100644 index 00000000..0401f2ea --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-3-3.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-F5V-T6B + rule|text|R-W1D-J2Y + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/input.efx new file mode 100644 index 00000000..d8dc180e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/input.efx @@ -0,0 +1,51 @@ +// Test: Phase generation from IN clause combinations +// Verifies phases are only generated for used notice types +// and pattern order matches stage order within each phase +// +// Each stage has rules with different IN clause combinations: +// Stage 1: Rule 1 -> IN 1, 2 | Rule 2 -> IN 1, 3 +// Stage 2: Rule 1 -> IN 1, 3 | Rule 2 -> IN 2 +// Stage 3: Rule 1 -> IN 1, 3 | Rule 2 -> IN 3 +// +// Expected patterns (8 total): +// - stage-1-1: rules 1,2 | stage-1-2: rule 1 | stage-1-3: rule 2 +// - stage-2-1: rule 1 | stage-2-2: rule 2 | stage-2-3: rule 1 +// - stage-3-1: rule 1 | stage-3-3: rules 1,2 +// +// Expected phases: +// - eforms-1: stage-1-1, stage-2-1, stage-3-1 (notice type 1 in stages 1, 2, 3) +// - eforms-2: stage-1-2, stage-2-2 (notice type 2 in stages 1, 2) +// - eforms-3: stage-1-3, stage-2-3, stage-3-3 (notice type 3 in stages 1, 2, 3) + +---- STAGE 1 ---- + +WITH BT-00-Text + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 + + ASSERT BT-00-Text is not empty + AS WARNING R-X3F-N8W + FOR BT-00-Text IN 1, 3 + +---- STAGE 2 ---- + +WITH BT-00-Text + ASSERT BT-00-Number is present + AS ERROR R-Y2N-G7S + FOR BT-00-Text IN 1, 3 + + REPORT BT-00-Number > 0 + AS INFO R-D4K-P9M + FOR BT-00-Text IN 2 + +---- STAGE 3 ---- + +WITH BT-00-Text + ASSERT BT-00-Indicator is present + AS ERROR R-F5V-T6B + FOR BT-00-Text IN 1, 3 + + REPORT BT-00-Indicator == TRUE + AS INFO R-W1D-J2Y + FOR BT-00-Text IN 3 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/schematrons.json new file mode 100644 index 00000000..99bc16b3 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/schematrons.json @@ -0,0 +1,91 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1-1", + "type" : "dynamic", + "stage" : "1", + "filename" : "dynamic/validation-stage-1-1.sch" + }, { + "name" : "validation-stage-1-2", + "type" : "dynamic", + "stage" : "1", + "filename" : "dynamic/validation-stage-1-2.sch" + }, { + "name" : "validation-stage-1-3", + "type" : "dynamic", + "stage" : "1", + "filename" : "dynamic/validation-stage-1-3.sch" + }, { + "name" : "validation-stage-2-1", + "type" : "dynamic", + "stage" : "2", + "filename" : "dynamic/validation-stage-2-1.sch" + }, { + "name" : "validation-stage-2-3", + "type" : "dynamic", + "stage" : "2", + "filename" : "dynamic/validation-stage-2-3.sch" + }, { + "name" : "validation-stage-2-2", + "type" : "dynamic", + "stage" : "2", + "filename" : "dynamic/validation-stage-2-2.sch" + }, { + "name" : "validation-stage-3-1", + "type" : "dynamic", + "stage" : "3", + "filename" : "dynamic/validation-stage-3-1.sch" + }, { + "name" : "validation-stage-3-3", + "type" : "dynamic", + "stage" : "3", + "filename" : "dynamic/validation-stage-3-3.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1-1", + "type" : "static", + "stage" : "1", + "filename" : "static/validation-stage-1-1.sch" + }, { + "name" : "validation-stage-1-2", + "type" : "static", + "stage" : "1", + "filename" : "static/validation-stage-1-2.sch" + }, { + "name" : "validation-stage-1-3", + "type" : "static", + "stage" : "1", + "filename" : "static/validation-stage-1-3.sch" + }, { + "name" : "validation-stage-2-1", + "type" : "static", + "stage" : "2", + "filename" : "static/validation-stage-2-1.sch" + }, { + "name" : "validation-stage-2-3", + "type" : "static", + "stage" : "2", + "filename" : "static/validation-stage-2-3.sch" + }, { + "name" : "validation-stage-2-2", + "type" : "static", + "stage" : "2", + "filename" : "static/validation-stage-2-2.sch" + }, { + "name" : "validation-stage-3-1", + "type" : "static", + "stage" : "3", + "filename" : "static/validation-stage-3-1.sch" + }, { + "name" : "validation-stage-3-3", + "type" : "static", + "stage" : "3", + "filename" : "static/validation-stage-3-3.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/complete-validation.sch new file mode 100644 index 00000000..6d0cec74 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/complete-validation.sch @@ -0,0 +1,44 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-1.sch new file mode 100644 index 00000000..1388ac63 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-2.sch new file mode 100644 index 00000000..8609bb6a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-3.sch new file mode 100644 index 00000000..f4743399 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-1.sch new file mode 100644 index 00000000..ae353b65 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-Y2N-G7S + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-2.sch new file mode 100644 index 00000000..b2ac866a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-D4K-P9M + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-3.sch new file mode 100644 index 00000000..8dd1b04e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-2-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-Y2N-G7S + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-3-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-3-1.sch new file mode 100644 index 00000000..034aab3a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-3-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-F5V-T6B + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-3-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-3-3.sch new file mode 100644 index 00000000..0401f2ea --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-3-3.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-F5V-T6B + rule|text|R-W1D-J2Y + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/complete-validation.sch new file mode 100644 index 00000000..69c9c01d --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/complete-validation.sch @@ -0,0 +1,38 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + ../PathNode/TextField + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..8ef796f4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..fdac6faf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-3.sch new file mode 100644 index 00000000..0fb27c70 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/dynamic/validation-stage-1a-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/input.efx new file mode 100644 index 00000000..d4b919c9 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/input.efx @@ -0,0 +1,9 @@ +// Test: IN clause with explicit notice type list +// Verifies filtering rules to specific notice types (1, 2, 3) + +---- STAGE 1a ---- + +WITH ND-SubNode + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2, 3 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/schematrons.json new file mode 100644 index 00000000..6129ab5e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/schematrons.json @@ -0,0 +1,41 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1a-3", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-3.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1a-3", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-3.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/complete-validation.sch new file mode 100644 index 00000000..69c9c01d --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/complete-validation.sch @@ -0,0 +1,38 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + ../PathNode/TextField + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..8ef796f4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..fdac6faf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-3.sch new file mode 100644 index 00000000..0fb27c70 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_SpecificNoticeTypes/static/validation-stage-1a-3.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/complete-validation.sch new file mode 100644 index 00000000..1894b10e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/complete-validation.sch @@ -0,0 +1,48 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + ../NumberField + ../IndicatorField + ../PathNode/NumberField + ../PathNode/IndicatorField + ../PathNode/TextField + ../TextField + ../IndicatorField + ../TextField + ../NumberField + PathNode/NumberField + PathNode/IndicatorField + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..d7539077 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,19 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + rule|text|R-H9T-V5L + rule|text|R-B6J-C4R + + + rule|text|R-Y2N-G7S + rule|text|R-D4K-P9M + rule|text|R-Z8H-A3X + + + rule|text|R-F5V-T6B + rule|text|R-W1D-J2Y + rule|text|R-Q7G-E4Z + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..7d1c5d8f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,19 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + rule|text|R-H9T-V5L + rule|text|R-B6J-C4R + + + rule|text|R-Y2N-G7S + rule|text|R-D4K-P9M + rule|text|R-Z8H-A3X + + + rule|text|R-F5V-T6B + rule|text|R-W1D-J2Y + rule|text|R-Q7G-E4Z + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-1.sch new file mode 100644 index 00000000..5d3b4567 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-1.sch @@ -0,0 +1,12 @@ + + + + rule|text|R-M3C-U8N + rule|text|R-S9L-R5K + rule|text|R-N6P-I2F + + + rule|text|R-V4T-O7J + rule|text|R-A8Q-H3W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-2.sch new file mode 100644 index 00000000..c77af2bd --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-2.sch @@ -0,0 +1,12 @@ + + + + rule|text|R-M3C-U8N + rule|text|R-S9L-R5K + rule|text|R-N6P-I2F + + + rule|text|R-V4T-O7J + rule|text|R-A8Q-H3W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/input.efx new file mode 100644 index 00000000..93851c89 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/input.efx @@ -0,0 +1,71 @@ +// Test: Comprehensive coverage of EFX rules features +// Covers: multiple stages, multiple WITH per stage, multiple rules per WITH, +// mix of simple rules (no WHEN) and conditional rules (WHEN/OTHERWISE), +// mix of ASSERT and REPORT, OTHERWISE REPORT and OTHERWISE ASSERT + +---- STAGE 1a ---- + +WITH BT-00-Text + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 + REPORT BT-00-Text is empty + AS WARNING R-X3F-N8W + FOR BT-00-Text IN 1, 2 + WHEN BT-00-Text == 'open' + ASSERT BT-00-Number is present + AS ERROR R-H9T-V5L + FOR BT-00-Number IN 1, 2 + WHEN BT-00-Text == 'closed' + REPORT BT-00-Indicator is not present + AS INFO R-B6J-C4R + FOR BT-00-Indicator IN 1, 2 + +WITH ND-SubNode + WHEN BT-00-Text == 'pending' + ASSERT BT-00-Number is present + AS ERROR R-Y2N-G7S + FOR BT-00-Number IN 1, 2 + WHEN BT-00-Text == 'active' + ASSERT BT-00-Indicator == TRUE + AS WARNING R-D4K-P9M + FOR BT-00-Indicator IN 1, 2 + OTHERWISE REPORT TRUE + AS INFO R-Z8H-A3X + FOR BT-00-Text IN 1, 2 + +WITH BT-00-Number + WHEN BT-00-Number > 0 + REPORT BT-00-Text is not empty + AS INFO R-F5V-T6B + FOR BT-00-Text IN 1, 2 + WHEN BT-00-Number < 100 + REPORT BT-00-Indicator is present + AS WARNING R-W1D-J2Y + FOR BT-00-Indicator IN 1, 2 + OTHERWISE ASSERT FALSE + AS ERROR R-Q7G-E4Z + FOR BT-00-Number IN 1, 2 + +---- STAGE 2a ---- + +WITH BT-00-Indicator + REPORT BT-00-Indicator is present + AS INFO R-M3C-U8N + FOR BT-00-Indicator IN 1, 2 + ASSERT BT-00-Text is not empty + AS ERROR R-S9L-R5K + FOR BT-00-Text IN 1, 2 + WHEN BT-00-Indicator == TRUE + ASSERT BT-00-Number > 0 + AS WARNING R-N6P-I2F + FOR BT-00-Number IN 1, 2 + +WITH ND-Root + WHEN BT-00-Text is present + REPORT BT-00-Number is present + AS INFO R-V4T-O7J + FOR BT-00-Number IN 1, 2 + OTHERWISE ASSERT BT-00-Indicator is present + AS ERROR R-A8Q-H3W + FOR BT-00-Indicator IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/schematrons.json new file mode 100644 index 00000000..e0b05d13 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/schematrons.json @@ -0,0 +1,51 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-2a-1", + "type" : "dynamic", + "stage" : "2a", + "filename" : "dynamic/validation-stage-2a-1.sch" + }, { + "name" : "validation-stage-2a-2", + "type" : "dynamic", + "stage" : "2a", + "filename" : "dynamic/validation-stage-2a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-2a-1", + "type" : "static", + "stage" : "2a", + "filename" : "static/validation-stage-2a-1.sch" + }, { + "name" : "validation-stage-2a-2", + "type" : "static", + "stage" : "2a", + "filename" : "static/validation-stage-2a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/complete-validation.sch new file mode 100644 index 00000000..1894b10e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/complete-validation.sch @@ -0,0 +1,48 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + ../NumberField + ../IndicatorField + ../PathNode/NumberField + ../PathNode/IndicatorField + ../PathNode/TextField + ../TextField + ../IndicatorField + ../TextField + ../NumberField + PathNode/NumberField + PathNode/IndicatorField + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..d7539077 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-1.sch @@ -0,0 +1,19 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + rule|text|R-H9T-V5L + rule|text|R-B6J-C4R + + + rule|text|R-Y2N-G7S + rule|text|R-D4K-P9M + rule|text|R-Z8H-A3X + + + rule|text|R-F5V-T6B + rule|text|R-W1D-J2Y + rule|text|R-Q7G-E4Z + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..7d1c5d8f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-2.sch @@ -0,0 +1,19 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + rule|text|R-H9T-V5L + rule|text|R-B6J-C4R + + + rule|text|R-Y2N-G7S + rule|text|R-D4K-P9M + rule|text|R-Z8H-A3X + + + rule|text|R-F5V-T6B + rule|text|R-W1D-J2Y + rule|text|R-Q7G-E4Z + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-1.sch new file mode 100644 index 00000000..5d3b4567 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-1.sch @@ -0,0 +1,12 @@ + + + + rule|text|R-M3C-U8N + rule|text|R-S9L-R5K + rule|text|R-N6P-I2F + + + rule|text|R-V4T-O7J + rule|text|R-A8Q-H3W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-2.sch new file mode 100644 index 00000000..c77af2bd --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-2.sch @@ -0,0 +1,12 @@ + + + + rule|text|R-M3C-U8N + rule|text|R-S9L-R5K + rule|text|R-N6P-I2F + + + rule|text|R-V4T-O7J + rule|text|R-A8Q-H3W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_FromSampleRulesFile/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_FromSampleRulesFile/input.efx new file mode 100644 index 00000000..cd4e4844 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_FromSampleRulesFile/input.efx @@ -0,0 +1,81 @@ +// Sample EFX Rules file for testing the EFX Rules to Schematron transpiler +// This file tests all major features of the transpiler implementation +// Uses only fields and nodes available in the mock symbol resolver + +// Schema-level variable declarations (global variables) +LET text : $sdkVersion = "2.0.0"; +LET text : $testValue = "test"; + +---- STAGE 1a ---- + +// Simple rule with FOR clause +WITH ND-Root +ASSERT BT-00-Text is present +AS ERROR R-K7P-M2Q +FOR BT-00-Text IN *; + +// Rule with WHEN clause +WITH ND-SubNode +WHEN BT-00-Indicator is present +ASSERT BT-00-Text is present +AS ERROR R-X3F-N8W +FOR BT-00-Text IN *; + +// Rule with WHEN/OTHERWISE +WITH ND-SubNode +WHEN BT-00-Indicator +ASSERT BT-00-Text is present +AS ERROR R-H9T-V5L +FOR BT-00-Text IN * +OTHERWISE +ASSERT BT-00-Text is not present +AS WARNING R-B6J-C4R +FOR BT-00-Text IN *; + +---- STAGE 1b ---- + +// Rule with IN clause - single notice type +WITH ND-Root +ASSERT BT-00-Code is present +AS ERROR R-Y2N-G7S +FOR BT-00-Code +IN 1; + +// Rule with IN clause - multiple notice types +WITH ND-SubNode +ASSERT BT-00-Indicator is present +AS ERROR R-D4K-P9M +FOR BT-00-Indicator +IN 1, 2, 3; + +---- STAGE 2a ---- + +LET text : $rootText = BT-00-Text; + +// Complex WHEN condition with variable +WITH ND-SubNode +WHEN $rootText is not empty +ASSERT BT-00-Indicator is present +AS ERROR R-F5V-T6B +FOR BT-00-Indicator IN *; + +// Rule checking boolean value +WITH ND-Root +ASSERT BT-00-Indicator +AS ERROR R-W1D-J2Y +FOR BT-00-Indicator IN *; + +---- STAGE 3a ---- + +// Rule with text comparison +WITH ND-SubSubNode +WHEN BT-00-Text == "expected" +ASSERT BT-00-Indicator is present +AS WARNING R-M3C-U8N +FOR BT-00-Indicator IN *; + +// Rule with code check +WITH ND-Root +ASSERT BT-00-Code in ("code1", "code2", "code3") +AS ERROR R-S9L-R5K +FOR BT-00-Code IN *; diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/complete-validation.sch new file mode 100644 index 00000000..aa333365 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/complete-validation.sch @@ -0,0 +1,39 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextField + ../../PathNode/TextField + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..b0b611f0 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..ec3e3a08 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1b-1.sch new file mode 100644 index 00000000..962e59b4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1b-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1b-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1b-2.sch new file mode 100644 index 00000000..f3861353 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/dynamic/validation-stage-1b-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/input.efx new file mode 100644 index 00000000..23196a37 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/input.efx @@ -0,0 +1,16 @@ +// Test: Multiple STAGE sections in one file +// Verifies that stages 1a and 1b generate separate patterns + +---- STAGE 1a ---- + +WITH ND-Root + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 + +---- STAGE 1b ---- + +WITH ND-SubSubNode + ASSERT BT-00-Text is present + AS ERROR R-X3F-N8W + FOR BT-00-Text IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/schematrons.json new file mode 100644 index 00000000..1cbb9f4a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/schematrons.json @@ -0,0 +1,51 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1b-1", + "type" : "dynamic", + "stage" : "1b", + "filename" : "dynamic/validation-stage-1b-1.sch" + }, { + "name" : "validation-stage-1b-2", + "type" : "dynamic", + "stage" : "1b", + "filename" : "dynamic/validation-stage-1b-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1b-1", + "type" : "static", + "stage" : "1b", + "filename" : "static/validation-stage-1b-1.sch" + }, { + "name" : "validation-stage-1b-2", + "type" : "static", + "stage" : "1b", + "filename" : "static/validation-stage-1b-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/complete-validation.sch new file mode 100644 index 00000000..aa333365 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/complete-validation.sch @@ -0,0 +1,39 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextField + ../../PathNode/TextField + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..b0b611f0 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..ec3e3a08 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1b-1.sch new file mode 100644 index 00000000..962e59b4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1b-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1b-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1b-2.sch new file mode 100644 index 00000000..f3861353 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testStage_Multiple_GeneratesSeparatePatterns/static/validation-stage-1b-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/complete-validation.sch new file mode 100644 index 00000000..3058014c --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/complete-validation.sch @@ -0,0 +1,36 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextField + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..95714f0c --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..1db07a91 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/input.efx new file mode 100644 index 00000000..2f0d9ce1 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/input.efx @@ -0,0 +1,13 @@ +// Test: Global (schema-level) variable declarations +// Verifies LET declarations before stages become elements in schema root +// Global variables appear before elements in complete-validation.sch + +LET text : $sdkVersion = "2.0.0"; +LET text : $noticeType = "16"; + +---- STAGE 1a ---- + +WITH ND-Root + ASSERT $sdkVersion is not empty + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/complete-validation.sch new file mode 100644 index 00000000..3058014c --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/complete-validation.sch @@ -0,0 +1,36 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextField + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..95714f0c --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..1db07a91 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/complete-validation.sch new file mode 100644 index 00000000..361318bf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/complete-validation.sch @@ -0,0 +1,28 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..9961a885 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1b-1.sch new file mode 100644 index 00000000..0e48cbc8 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1b-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/input.efx new file mode 100644 index 00000000..e39d8fb4 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/input.efx @@ -0,0 +1,11 @@ +---- STAGE 1a ---- +WITH text : $stageVar = "first", BT-00-Text +ASSERT $stageVar is not empty +AS ERROR R-K7P-M2Q +FOR BT-00-Text IN 1; + +---- STAGE 1b ---- +WITH text : $stageVar = "second", BT-00-Text +ASSERT $stageVar is not empty +AS ERROR R-X3F-N8W +FOR BT-00-Text IN 1; \ No newline at end of file diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/schematrons.json new file mode 100644 index 00000000..8de27c59 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1b-1", + "type" : "dynamic", + "stage" : "1b", + "filename" : "dynamic/validation-stage-1b-1.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1b-1", + "type" : "static", + "stage" : "1b", + "filename" : "static/validation-stage-1b-1.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/complete-validation.sch new file mode 100644 index 00000000..361318bf --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/complete-validation.sch @@ -0,0 +1,28 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..9961a885 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1b-1.sch new file mode 100644 index 00000000..0e48cbc8 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1b-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/complete-validation.sch new file mode 100644 index 00000000..d0eac516 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + ../PathNode/TextField + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..7a38b292 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..a15ae36f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/input.efx new file mode 100644 index 00000000..06adc478 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/input.efx @@ -0,0 +1,10 @@ +// Test: WHEN conditional clause without OTHERWISE +// Verifies conditional assertion that only fires when WHEN condition is met + +---- STAGE 1a ---- + +WITH ND-SubNode + WHEN BT-00-Text is present + ASSERT BT-00-Text == 'open' + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/complete-validation.sch new file mode 100644 index 00000000..d0eac516 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + ../PathNode/TextField + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..7a38b292 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..a15ae36f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_SimpleCondition/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..ca5be220 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..5904c540 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,8 @@ + + + + rule|text|R-X3F-N8W + rule|text|R-H9T-V5L + rule|text|R-B6J-C4R + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/input.efx new file mode 100644 index 00000000..e1a93042 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/input.efx @@ -0,0 +1,26 @@ +// Test: Multiple WHEN conditions with OTHERWISE fallback +// Verifies conditional logic with varying notice types and mixed ASSERT/REPORT: +// - WHEN ASSERT, WHEN REPORT, OTHERWISE REPORT +// - Different IN clauses per condition + +---- STAGE 1a ---- + +WITH BT-00-Text + WHEN BT-00-Text == 'open' + ASSERT BT-00-Number is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1 + + WHEN BT-00-Text == 'restricted' + REPORT BT-00-Number > 0 + AS INFO R-X3F-N8W + FOR BT-00-Text IN 1, 2 + + WHEN BT-00-Text == 'negotiated' + ASSERT BT-00-Indicator is present + AS WARNING R-H9T-V5L + FOR BT-00-Text IN 2 + + OTHERWISE REPORT BT-00-Text is not empty + AS INFO R-B6J-C4R + FOR BT-00-Text IN 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..ca5be220 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + rule|text|R-K7P-M2Q + rule|text|R-X3F-N8W + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..5904c540 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-2.sch @@ -0,0 +1,8 @@ + + + + rule|text|R-X3F-N8W + rule|text|R-H9T-V5L + rule|text|R-B6J-C4R + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..52e906c8 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..d1261e77 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/input.efx new file mode 100644 index 00000000..edf7d629 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/input.efx @@ -0,0 +1,10 @@ +// Test: Context variable syntax (WITH context : $var = FieldRef) +// Verifies context : $ctx = BT-00-Text declares $ctx with value="." +// No diagnostics when context == subject + +---- STAGE 1a ---- + +WITH context : $ctx = BT-00-Text + ASSERT $ctx is not empty + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/complete-validation.sch new file mode 100644 index 00000000..e77e22fe --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/complete-validation.sch @@ -0,0 +1,30 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..52e906c8 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..d1261e77 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-2.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/complete-validation.sch new file mode 100644 index 00000000..ed3741d1 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextField + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..b0b611f0 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..ec3e3a08 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/input.efx new file mode 100644 index 00000000..7fcab25e --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/input.efx @@ -0,0 +1,10 @@ +// Test: Root context declaration syntax (WITH /) +// Verifies the root context '/' resolves to '/*' in XPath +// Diagnostics generated because context (/) != subject (BT-00-Text) + +---- STAGE 1a ---- + +WITH / + ASSERT BT-00-Text is present + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1, 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/schematrons.json new file mode 100644 index 00000000..2bd84096 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/schematrons.json @@ -0,0 +1,31 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/complete-validation.sch new file mode 100644 index 00000000..ed3741d1 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + PathNode/TextField + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..b0b611f0 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/validation-stage-1a-1.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..ec3e3a08 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_RootContextShortcut/static/validation-stage-1a-2.sch @@ -0,0 +1,6 @@ + + + + rule|text|R-K7P-M2Q + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/complete-validation.sch new file mode 100644 index 00000000..67133b70 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-1.sch new file mode 100644 index 00000000..48fb4b2b --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-1.sch @@ -0,0 +1,38 @@ + + + + + + + + + + + + rule|text|R-K7P-M2Q + + + rule|text|R-Y2N-G7S + + + rule|text|R-F5V-T6B + + + + rule|text|R-M3C-U8N + + + + + rule|text|R-V4T-O7J + + + + rule|text|R-G2M-X6D + + + + + rule|text|R-C1V-Z4H + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-2.sch new file mode 100644 index 00000000..49dd1c75 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-2.sch @@ -0,0 +1,38 @@ + + + + + + + + + + + + rule|text|R-X3F-N8W + + + rule|text|R-D4K-P9M + + + rule|text|R-W1D-J2Y + + + + rule|text|R-S9L-R5K + + + + + rule|text|R-A8Q-H3W + + + + rule|text|R-J5B-Y9S + + + + + rule|text|R-T6N-K8R + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-1.sch new file mode 100644 index 00000000..137a5cd9 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-E3F-L2G + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-2.sch new file mode 100644 index 00000000..6b73c39f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-2.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-P9C-W5Y + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/input.efx new file mode 100644 index 00000000..8ce5b661 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/input.efx @@ -0,0 +1,95 @@ +// Test: Variable declarations in WITH clause and at stage level +// Each WITH has multiple asserts with different IN clauses to verify +// variables work correctly when rules end up in different phases. +// Also tests stage-level variables declared at beginning of stage. + +---- STAGE 1a ---- + +// Stage variables at beginning of stage (pattern-level, available to all rules) +LET text : $stageVar1 = "S1"; +LET text : $stageVar2 = "S2"; + +// Case 1: No variables (baseline) - uses stage variable +WITH BT-00-Text + ASSERT $stageVar1 is not empty + AS ERROR R-K7P-M2Q + FOR BT-00-Text IN 1 + + ASSERT BT-00-Text is not empty + AS ERROR R-X3F-N8W + FOR BT-00-Text IN 2 + +// Case 2: Single variable before context (pattern-level) +WITH text : $before1 = "b1", BT-00-Number + ASSERT $before1 is not empty + AS ERROR R-Y2N-G7S + FOR BT-00-Number IN 1 + + ASSERT $before1 != "x" + AS ERROR R-D4K-P9M + FOR BT-00-Number IN 2 + +// Case 3: Multiple variables before context (pattern-level) +WITH text : $preA = "A", text : $preB = "B", BT-00-Indicator + ASSERT $preA is not empty and $preB is not empty + AS ERROR R-F5V-T6B + FOR BT-00-Indicator IN 1 + + ASSERT $preA != $preB + AS ERROR R-W1D-J2Y + FOR BT-00-Indicator IN 2 + +// Case 4: Single variable after context (rule-level) - uses stage variable +WITH BT-00-Text, text : $after1 = "a1" + ASSERT $stageVar2 is not empty and $after1 is not empty + AS ERROR R-M3C-U8N + FOR BT-00-Text IN 1 + + ASSERT $after1 != "x" + AS ERROR R-S9L-R5K + FOR BT-00-Text IN 2 + +// Case 5: Multiple variables after context (rule-level) +WITH BT-00-Number, text : $postX = "X", text : $postY = "Y" + ASSERT $postX is not empty and $postY is not empty + AS ERROR R-V4T-O7J + FOR BT-00-Number IN 1 + + ASSERT $postX != $postY + AS ERROR R-A8Q-H3W + FOR BT-00-Number IN 2 + +// Case 6: One before, one after (mixed pattern/rule level) +WITH text : $left = "L", BT-00-Indicator, text : $right = "R" + ASSERT $left is not empty and $right is not empty + AS ERROR R-G2M-X6D + FOR BT-00-Indicator IN 1 + + ASSERT $left != $right + AS ERROR R-J5B-Y9S + FOR BT-00-Indicator IN 2 + +// Case 7: Multiple before and after (full mixed) - uses both stage variables +WITH text : $p1 = "P1", text : $p2 = "P2", BT-00-Text, text : $r1 = "R1", text : $r2 = "R2" + ASSERT $stageVar1 is not empty and $stageVar2 is not empty and $p1 is not empty + AS ERROR R-C1V-Z4H + FOR BT-00-Text IN 1 + + ASSERT $p1 != $r1 and $p2 != $r2 + AS ERROR R-T6N-K8R + FOR BT-00-Text IN 2 + +---- STAGE 1b ---- + +// Different stage variables - should only appear in stage 1b patterns +LET text : $stage1bVar = "1B"; + +// Simple rule using stage 1b variable +WITH BT-00-Text + ASSERT $stage1bVar is not empty + AS ERROR R-E3F-L2G + FOR BT-00-Text IN 1 + + ASSERT BT-00-Text is present + AS ERROR R-P9C-W5Y + FOR BT-00-Text IN 2 diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/schematrons.json b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/schematrons.json new file mode 100644 index 00000000..1cbb9f4a --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/schematrons.json @@ -0,0 +1,51 @@ +{ + "schematrons" : [ { + "name" : "complete-validation", + "type" : "dynamic", + "filename" : "dynamic/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "dynamic", + "stage" : "1a", + "filename" : "dynamic/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1b-1", + "type" : "dynamic", + "stage" : "1b", + "filename" : "dynamic/validation-stage-1b-1.sch" + }, { + "name" : "validation-stage-1b-2", + "type" : "dynamic", + "stage" : "1b", + "filename" : "dynamic/validation-stage-1b-2.sch" + }, { + "name" : "complete-validation", + "type" : "static", + "filename" : "static/complete-validation.sch" + }, { + "name" : "validation-stage-1a-1", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-1.sch" + }, { + "name" : "validation-stage-1a-2", + "type" : "static", + "stage" : "1a", + "filename" : "static/validation-stage-1a-2.sch" + }, { + "name" : "validation-stage-1b-1", + "type" : "static", + "stage" : "1b", + "filename" : "static/validation-stage-1b-1.sch" + }, { + "name" : "validation-stage-1b-2", + "type" : "static", + "stage" : "1b", + "filename" : "static/validation-stage-1b-2.sch" + } ] +} diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/complete-validation.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/complete-validation.sch new file mode 100644 index 00000000..67133b70 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/complete-validation.sch @@ -0,0 +1,34 @@ + + + + eForms schematron rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-1.sch new file mode 100644 index 00000000..48fb4b2b --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-1.sch @@ -0,0 +1,38 @@ + + + + + + + + + + + + rule|text|R-K7P-M2Q + + + rule|text|R-Y2N-G7S + + + rule|text|R-F5V-T6B + + + + rule|text|R-M3C-U8N + + + + + rule|text|R-V4T-O7J + + + + rule|text|R-G2M-X6D + + + + + rule|text|R-C1V-Z4H + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-2.sch new file mode 100644 index 00000000..49dd1c75 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-2.sch @@ -0,0 +1,38 @@ + + + + + + + + + + + + rule|text|R-X3F-N8W + + + rule|text|R-D4K-P9M + + + rule|text|R-W1D-J2Y + + + + rule|text|R-S9L-R5K + + + + + rule|text|R-A8Q-H3W + + + + rule|text|R-J5B-Y9S + + + + + rule|text|R-T6N-K8R + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-1.sch new file mode 100644 index 00000000..137a5cd9 --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-1.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-E3F-L2G + + diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-2.sch new file mode 100644 index 00000000..6b73c39f --- /dev/null +++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-2.sch @@ -0,0 +1,7 @@ + + + + + rule|text|R-P9C-W5Y + + diff --git a/src/test/resources/json/fields-sdk2.json b/src/test/resources/json/fields-sdk2.json index 0b4b1566..85338bc2 100644 --- a/src/test/resources/json/fields-sdk2.json +++ b/src/test/resources/json/fields-sdk2.json @@ -219,4 +219,12 @@ "parentNodeId": "ND-SubSubNode", "xpathAbsolute": "/*/SubNode/SubSubNode/SubTextField[0 = 0]", "xpathRelative": "SubTextField" + }, + { + "id": "BT-00(a)-Text", + "alias": "textFieldWithParens", + "type": "text", + "parentNodeId": "ND-Root", + "xpathAbsolute": "/*/PathNode/TextFieldA", + "xpathRelative": "PathNode/TextFieldA" }]