|
1 | 1 | /* |
2 | | - * Copyright 2022 European Union |
| 2 | + * Copyright 2025 European Union |
3 | 3 | * |
4 | 4 | * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European |
5 | 5 | * Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in |
|
20 | 20 | import java.util.LinkedHashMap; |
21 | 21 | import java.util.List; |
22 | 22 | import java.util.Map; |
| 23 | +import java.util.Set; |
23 | 24 | import java.util.stream.Collectors; |
24 | 25 |
|
25 | 26 | import org.slf4j.Logger; |
|
32 | 33 | import eu.europa.ted.eforms.sdk.component.SdkComponent; |
33 | 34 | import eu.europa.ted.eforms.sdk.component.SdkComponentType; |
34 | 35 | import eu.europa.ted.efx.interfaces.ValidatorGenerator; |
35 | | -import eu.europa.ted.efx.model.rules.CompleteValidation; |
| 36 | +import eu.europa.ted.efx.model.rules.ValidationPlan; |
| 37 | +import eu.europa.ted.efx.model.rules.RuleNature; |
36 | 38 | import eu.europa.ted.efx.model.rules.ValidationStage; |
37 | 39 | import eu.europa.ted.efx.model.variables.Variable; |
38 | 40 | import freemarker.template.Configuration; |
@@ -69,31 +71,59 @@ public SchematronGenerator() { |
69 | 71 | // #region ValidatorMarkupGenerator Implementation |
70 | 72 |
|
71 | 73 | @Override |
72 | | - public Map<String, String> generateOutput(CompleteValidation completeValidation) { |
73 | | - logger.debug("Generating Schematron output from {} stages", completeValidation.getStages().size()); |
| 74 | + public Map<String, String> generateOutput(ValidationPlan validationPlan) { |
| 75 | + logger.debug("Generating Schematron output from {} stages", validationPlan.getStages().size()); |
74 | 76 |
|
75 | 77 | // Create local state for this generation run |
76 | 78 | SchematronSchema schema = new SchematronSchema("eForms schematron rules"); |
77 | 79 | List<SchematronPattern> patterns = new ArrayList<>(); |
78 | 80 | Map<String, SchematronDiagnostic> diagnosticsMap = new LinkedHashMap<>(); |
79 | 81 |
|
| 82 | + // Add endpoint params to schema |
| 83 | + for (Map.Entry<String, String> endpoint : validationPlan.getEndpoints().entrySet()) { |
| 84 | + String url = endpoint.getValue() != null ? endpoint.getValue() : ""; |
| 85 | + SchematronParam param = new SchematronParam("apiUrl-" + endpoint.getKey(), "'" + url + "'"); |
| 86 | + schema.addParam(param); |
| 87 | + logger.debug("Added endpoint param: {} = {}", param.getName(), param.getValue()); |
| 88 | + } |
| 89 | + |
80 | 90 | // Add global variables to schema |
81 | | - for (Variable variable : completeValidation.getGlobalVariables()) { |
82 | | - String xpathValue = variable.initializationExpression.getScript(); |
83 | | - SchematronLet globalVar = new SchematronLet(variable.name, xpathValue); |
84 | | - schema.addGlobalVariable(globalVar); |
85 | | - logger.debug("Added global variable: {} = {}", variable.name, xpathValue); |
| 91 | + for (Variable variable : validationPlan.getVariables()) { |
| 92 | + SchematronLet letElement = new SchematronLet(variable); |
| 93 | + schema.addLetElement(letElement); |
| 94 | + logger.debug("Added global variable: {} = {}", variable.name, variable.initializationExpression.getScript()); |
86 | 95 | } |
87 | 96 |
|
88 | 97 | // Transform intermediate model (ValidationStage) to Schematron model (SchematronPattern) |
89 | | - transformStagesToPatterns(completeValidation.getStages(), patterns, diagnosticsMap); |
| 98 | + for (ValidationStage stage : validationPlan.getStages()) { |
| 99 | + if (stage.containsUniversalRules()) { |
| 100 | + SchematronPattern sharedPattern = new SchematronPattern(stage); |
| 101 | + if (sharedPattern.hasRules()) { |
| 102 | + patterns.add(sharedPattern); |
| 103 | + diagnosticsMap.putAll(sharedPattern.getDiagnostics()); |
| 104 | + logger.debug("Created shared pattern {} for stage {}", |
| 105 | + sharedPattern.getId(), stage.getName()); |
| 106 | + } |
| 107 | + } |
| 108 | + for (String noticeSubtype : stage.getNoticeSubtypes()) { |
| 109 | + SchematronPattern pattern = new SchematronPattern(stage, noticeSubtype); |
| 110 | + if (pattern.hasRules()) { |
| 111 | + patterns.add(pattern); |
| 112 | + diagnosticsMap.putAll(pattern.getDiagnostics()); |
| 113 | + logger.debug("Created pattern {} for stage {} / notice subtype {}", |
| 114 | + pattern.getId(), stage.getName(), noticeSubtype); |
| 115 | + } |
| 116 | + } |
| 117 | + } |
90 | 118 |
|
91 | 119 | // Add collected diagnostics to schema |
92 | | - addDiagnosticsToSchema(diagnosticsMap, schema); |
| 120 | + for (SchematronDiagnostic diagnostic : diagnosticsMap.values()) { |
| 121 | + schema.addDiagnostic(diagnostic); |
| 122 | + } |
93 | 123 |
|
94 | 124 | // Generate all output files |
95 | 125 | try { |
96 | | - return generateOutputFiles(completeValidation.getNoticeSubtypes(), patterns, schema); |
| 126 | + return this.generateOutputFiles(validationPlan.getNoticeSubtypes(), patterns, schema); |
97 | 127 | } catch (IOException e) { |
98 | 128 | throw new RuntimeException("Failed to generate Schematron output", e); |
99 | 129 | } |
@@ -135,61 +165,20 @@ public String generatePattern(SchematronPattern pattern, SchematronOutputConfig |
135 | 165 |
|
136 | 166 | Map<String, Object> model = new HashMap<>(); |
137 | 167 | model.put("id", pattern.getId()); |
138 | | - model.put("variables", pattern.getVariables()); |
| 168 | + List<String> tags = config.ruleNatures().stream() |
| 169 | + .map(Enum::name).collect(Collectors.toList()); |
| 170 | + List<SchematronLet> letElements = pattern.getLetElements().stream() |
| 171 | + .filter(v -> tags.contains(v.getTag())).collect(Collectors.toList()); |
| 172 | + model.put("letElements", letElements); |
139 | 173 | model.put("rules", pattern.getRules()); |
140 | | - model.put("tags", config.ruleNatures().stream() |
141 | | - .map(Enum::name) |
142 | | - .collect(Collectors.toList())); |
| 174 | + model.put("tags", tags); |
143 | 175 |
|
144 | 176 | template.process(model, writer); |
145 | 177 | return writer.toString(); |
146 | 178 | } |
147 | 179 |
|
148 | 180 | // #endregion Freemarker Template Methods |
149 | 181 |
|
150 | | - // #region Transformation Methods |
151 | | - |
152 | | - /** |
153 | | - * Transforms validation stages into Schematron patterns. |
154 | | - * For each stage, first creates a shared pattern for rules that apply to all subtypes, |
155 | | - * then creates subtype-specific patterns for the remaining rules. |
156 | | - */ |
157 | | - private void transformStagesToPatterns(List<ValidationStage> stages, |
158 | | - List<SchematronPattern> patterns, Map<String, SchematronDiagnostic> diagnosticsMap) { |
159 | | - for (ValidationStage stage : stages) { |
160 | | - // Create shared pattern for rules that apply to all subtypes |
161 | | - if (stage.containsUniversalRules()) { |
162 | | - SchematronPattern sharedPattern = new SchematronPattern(stage); |
163 | | - if (sharedPattern.hasRules()) { |
164 | | - patterns.add(sharedPattern); |
165 | | - diagnosticsMap.putAll(sharedPattern.getDiagnostics()); |
166 | | - logger.debug("Created shared pattern {} for stage {}", |
167 | | - sharedPattern.getId(), stage.getName()); |
168 | | - } |
169 | | - } |
170 | | - |
171 | | - // Create subtype-specific patterns for remaining rules |
172 | | - for (String noticeSubtype : stage.getNoticeSubtypes()) { |
173 | | - SchematronPattern pattern = new SchematronPattern(stage, noticeSubtype); |
174 | | - if (pattern.hasRules()) { |
175 | | - patterns.add(pattern); |
176 | | - diagnosticsMap.putAll(pattern.getDiagnostics()); |
177 | | - logger.debug("Created pattern {} for stage {} / notice subtype {}", |
178 | | - pattern.getId(), stage.getName(), noticeSubtype); |
179 | | - } |
180 | | - } |
181 | | - } |
182 | | - } |
183 | | - |
184 | | - private void addDiagnosticsToSchema(Map<String, SchematronDiagnostic> diagnosticsMap, |
185 | | - SchematronSchema schema) { |
186 | | - for (SchematronDiagnostic diagnostic : diagnosticsMap.values()) { |
187 | | - schema.addDiagnostic(diagnostic); |
188 | | - } |
189 | | - } |
190 | | - |
191 | | - // #endregion Transformation Methods |
192 | | - |
193 | 182 | // #region Output Generation Methods |
194 | 183 |
|
195 | 184 | /** |
@@ -219,11 +208,11 @@ private Map<String, String> generateOutputFiles(List<String> noticeSubtypeIds, |
219 | 208 |
|
220 | 209 | try { |
221 | 210 | for (SchematronOutputConfig config : configs) { |
222 | | - generateOutputForConfig(config, noticeSubtypeIds, patterns, baseSchema, outputFiles, schematronsMetadata); |
| 211 | + this.generateOutputForConfig(config, noticeSubtypeIds, patterns, baseSchema, outputFiles, schematronsMetadata); |
223 | 212 | } |
224 | 213 |
|
225 | 214 | // Generate schematrons.json with entries from all configurations |
226 | | - String schematronsJson = generateSchematronsJson(schematronsMetadata); |
| 215 | + String schematronsJson = this.generateSchematronsJson(schematronsMetadata); |
227 | 216 | outputFiles.put("schematrons.json", schematronsJson); |
228 | 217 |
|
229 | 218 | logger.debug("Generated {} Schematron files", outputFiles.size()); |
@@ -251,8 +240,18 @@ private void generateOutputForConfig( |
251 | 240 |
|
252 | 241 | // Create a fresh schema for this configuration |
253 | 242 | SchematronSchema schema = new SchematronSchema(baseSchema.getTitle()); |
254 | | - for (SchematronLet globalVar : baseSchema.getGlobalVariables()) { |
255 | | - schema.addGlobalVariable(globalVar); |
| 243 | + // API endpoint params are only relevant for configurations that include dynamic rules |
| 244 | + if (config.ruleNatures().contains(RuleNature.DYNAMIC)) { |
| 245 | + for (SchematronParam param : baseSchema.getParams()) { |
| 246 | + schema.addParam(param); |
| 247 | + } |
| 248 | + } |
| 249 | + Set<String> configTags = config.ruleNatures().stream() |
| 250 | + .map(Enum::name).collect(Collectors.toSet()); |
| 251 | + for (SchematronLet letElement : baseSchema.getLetElements()) { |
| 252 | + if (configTags.contains(letElement.getTag())) { |
| 253 | + schema.addLetElement(letElement); |
| 254 | + } |
256 | 255 | } |
257 | 256 | for (SchematronDiagnostic diagnostic : baseSchema.getDiagnostics()) { |
258 | 257 | schema.addDiagnostic(diagnostic); |
@@ -307,9 +306,9 @@ private void generateOutputForConfig( |
307 | 306 | } |
308 | 307 |
|
309 | 308 | // Generate complete-validation.sch for this configuration |
310 | | - String completeValidation = generateCompleteValidation(schema); |
| 309 | + String completeValidationContent = generateCompleteValidation(schema); |
311 | 310 | String completeFilename = folderPrefix + "complete-validation.sch"; |
312 | | - outputFiles.put(completeFilename, completeValidation); |
| 311 | + outputFiles.put(completeFilename, completeValidationContent); |
313 | 312 |
|
314 | 313 | // Add complete-validation to metadata at the beginning of this config's entries |
315 | 314 | Map<String, Object> completeMetadata = new LinkedHashMap<>(); |
|
0 commit comments