Skip to content

Commit f66841d

Browse files
committed
Support to include a simplified XPath location in report items when the location is returned as a line position
1 parent f4c95ca commit f66841d

File tree

11 files changed

+121
-8
lines changed

11 files changed

+121
-8
lines changed

xmlvalidator-common/src/main/java/eu/europa/ec/itb/xml/ApplicationConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class ApplicationConfig extends eu.europa.ec.itb.validation.commons.confi
3535
private String defaultLocationAsPathDescription;
3636
private String defaultLocaleDescription;
3737
private String defaultAddInputToReportDescription;
38+
private String defaultShowLocationPathsDescription;
3839

3940
/**
4041
* @return True if caching is disabled for artifact pre-processing.
@@ -288,6 +289,20 @@ public void setDefaultAddInputToReportDescription(String defaultAddInputToReport
288289
this.defaultAddInputToReportDescription = defaultAddInputToReportDescription;
289290
}
290291

292+
/**
293+
* @return The web service description for the show location paths input.
294+
*/
295+
public String getDefaultShowLocationPathsDescription() {
296+
return defaultShowLocationPathsDescription;
297+
}
298+
299+
/**
300+
* @param defaultShowLocationPathsDescription The web service description for the show location paths input.
301+
*/
302+
public void setDefaultShowLocationPathsDescription(String defaultShowLocationPathsDescription) {
303+
this.defaultShowLocationPathsDescription = defaultShowLocationPathsDescription;
304+
}
305+
291306
/**
292307
* @return The default labels to use for the description of SOAP web service inputs.
293308
*/
@@ -314,6 +329,7 @@ public void init() {
314329
defaultLabels.put(ValidationConstants.INPUT_LOCATION_AS_PATH, defaultLocationAsPathDescription);
315330
defaultLabels.put(ValidationConstants.INPUT_LOCALE, defaultLocaleDescription);
316331
defaultLabels.put(ValidationConstants.INPUT_ADD_INPUT_TO_REPORT, defaultAddInputToReportDescription);
332+
defaultLabels.put(ValidationConstants.INPUT_SHOW_LOCATION_PATHS, defaultShowLocationPathsDescription);
317333
}
318334

319335
}

xmlvalidator-common/src/main/java/eu/europa/ec/itb/xml/DomainConfig.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class DomainConfig extends WebDomainConfig {
3232
private String mailInboundFolder;
3333
private boolean includeTestDefinition;
3434
private boolean includeAssertionID;
35+
private boolean includeLocationPath = false;
3536
private List<ContextFileConfig> contextFileConfigDefaultConfig;
3637
private Map<String, List<ContextFileConfig>> contextFileMap;
3738
private ContextFileCombinationTemplateConfig contextFileCombinationDefaultTemplate;
@@ -191,6 +192,20 @@ public ValidationArtifactInfo getSchematronInfo(String validationType) {
191192
return getArtifactInfo().get(validationType).get(ARTIFACT_TYPE_SCHEMATRON);
192193
}
193194

195+
/**
196+
* @return True if path locations should be included in validation report items.
197+
*/
198+
public boolean isIncludeLocationPath() {
199+
return includeLocationPath;
200+
}
201+
202+
/**
203+
* @param includeLocationPath True if path locations should be included in validation report items.
204+
*/
205+
public void setIncludeLocationPath(boolean includeLocationPath) {
206+
this.includeLocationPath = includeLocationPath;
207+
}
208+
194209
/**
195210
* @return True if tests should be included in validation report items.
196211
*/

xmlvalidator-common/src/main/java/eu/europa/ec/itb/xml/DomainConfigCache.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ protected void addDomainConfiguration(DomainConfig domainConfig, Configuration c
8484
domainConfig.setMailInboundFolder(config.getString("validator.mailInboundFolder", "INBOX"));
8585
domainConfig.setIncludeTestDefinition(config.getBoolean("validator.includeTestDefinition", true));
8686
domainConfig.setIncludeAssertionID(config.getBoolean("validator.includeAssertionID", true));
87+
domainConfig.setIncludeLocationPath(config.getBoolean("validator.includeLocationPath", false));
8788
// Context files - START
8889
domainConfig.setContextFileDefaultConfig(ParseUtils.parseValueList("validator.defaultContextFile", config, getContextFileMapper(domainConfig, true)));
8990
domainConfig.setContextFiles(parseTypeSpecificContextFiles("validator.contextFile", domainConfig.getType(), config, domainConfig));

xmlvalidator-common/src/main/java/eu/europa/ec/itb/xml/ValidationSpecs.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class ValidationSpecs {
6060
private List<ContextFileData> contextFiles;
6161
private boolean locationAsPath = false;
6262
private boolean addInputToReport = true;
63+
private Boolean showLocationPaths;
6364
private Path tempFolder;
6465
private List<SchematronFileInfo> schematronFilesToUse;
6566
private ApplicationContext applicationContext;
@@ -593,6 +594,16 @@ public boolean isLocationAsPath() {
593594
return locationAsPath;
594595
}
595596

597+
/**
598+
* @return True if a simplified XPath expression should be added to report item locations.
599+
*/
600+
public boolean isShowLocationPaths() {
601+
if (showLocationPaths == null) {
602+
showLocationPaths = domainConfig.isIncludeLocationPath();
603+
}
604+
return showLocationPaths;
605+
}
606+
596607
/**
597608
* @return True if the provided input should be added as context to the produced TAR report.
598609
*/
@@ -703,6 +714,17 @@ public Builder addInputToReport(boolean addInputToReport) {
703714
return this;
704715
}
705716

717+
/**
718+
* Whether locations will include a simplified XPath expression.
719+
*
720+
* @param showLocationPaths The flag's value.
721+
* @return The builder.
722+
*/
723+
public Builder showLocationPaths(boolean showLocationPaths) {
724+
instance.showLocationPaths = showLocationPaths;
725+
return this;
726+
}
727+
706728
/**
707729
* Set the temporary folder used for this validation run.
708730
*

xmlvalidator-common/src/main/java/eu/europa/ec/itb/xml/validation/SchematronReportHandler.java

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class SchematronReportHandler {
3939
private final SchematronOutputType svrlReport;
4040
private final boolean locationAsPath;
4141
private final LocalisationHelper localiser;
42+
private final boolean includeLocationPath;
4243
private NamespaceContext namespaceContext;
4344
private XPathFactory xpathFactory;
4445
private Boolean hasDefaultNamespace;
@@ -58,9 +59,11 @@ public class SchematronReportHandler {
5859
* @param includeAssertionID True if the assertion IDs per report item should be included.
5960
* @param locationAsPath True if report item locations should be XPath expressions. If not the line numbers will be
6061
* calculated and recorded instead.
62+
* @param includeLocationPath True to append the location XPath expression when the location is reported as the line number.
63+
* In case locationAsPath is true this flag is ignored.
6164
* @param localiser Helper class for translations.
6265
*/
63-
public SchematronReportHandler(Document node, SchematronOutputType svrl, boolean convertXPathExpressions, boolean includeTest, boolean includeAssertionID, boolean locationAsPath, LocalisationHelper localiser) {
66+
public SchematronReportHandler(Document node, SchematronOutputType svrl, boolean convertXPathExpressions, boolean includeTest, boolean includeAssertionID, boolean locationAsPath, boolean includeLocationPath, LocalisationHelper localiser) {
6467
this.node = node;
6568
this.svrlReport = svrl;
6669
report = new TAR();
@@ -71,6 +74,7 @@ public SchematronReportHandler(Document node, SchematronOutputType svrl, boolean
7174
this.convertXPathExpressions = convertXPathExpressions;
7275
this.includeTest = includeTest;
7376
this.includeAssertionID = includeAssertionID;
77+
this.includeLocationPath = includeLocationPath;
7478
this.locationAsPath = locationAsPath;
7579
this.localiser = localiser;
7680
}
@@ -153,9 +157,14 @@ private <T extends AbstractSVRLMessage> List<JAXBElement<TestAssertionReportType
153157
reportItem.setDescription(getMessageText(message));
154158
if (message.getLocation() != null && !message.getLocation().isBlank()) {
155159
if (locationAsPath) {
156-
reportItem.setLocation(message.getLocation());
160+
reportItem.setLocation(toPathForPresentation(message.getLocation()));
157161
} else {
158-
reportItem.setLocation(ValidationConstants.INPUT_XML+":" + this.getLineNumberFromXPath(message.getLocation()) + ":0");
162+
LocationInfo locationInfo = getLocationInfo(message.getLocation());
163+
if (includeLocationPath) {
164+
reportItem.setLocation("%s:%s:0|%s".formatted(ValidationConstants.INPUT_XML, locationInfo.lineNumber(), locationInfo.path()));
165+
} else {
166+
reportItem.setLocation("%s:%s:0".formatted(ValidationConstants.INPUT_XML, locationInfo.lineNumber()));
167+
}
159168
}
160169
}
161170
if (message.getTest() != null && includeTest) {
@@ -261,11 +270,12 @@ private XPathFactory getXPathFactory() {
261270
* @param xpathExpression The expression.
262271
* @return The line number.
263272
*/
264-
private String getLineNumberFromXPath(String xpathExpression) {
273+
private LocationInfo getLocationInfo(String xpathExpression) {
265274
String xpathExpressionConverted = convertToXPathExpression(xpathExpression);
266275
XPath xPath = getXPathFactory().newXPath();
267276
xPath.setNamespaceContext(getNamespaceContext());
268277
Node locatedNode;
278+
String lineNumber;
269279
try {
270280
locatedNode = (Node)xPath.evaluate(xpathExpressionConverted, this.node, XPathConstants.NODE);
271281
if (locatedNode == null) {
@@ -275,13 +285,30 @@ private String getLineNumberFromXPath(String xpathExpression) {
275285
}
276286
}
277287
if (locatedNode == null) {
278-
return "0";
288+
lineNumber = "0";
279289
} else {
280-
return (String)locatedNode.getUserData("lineNumber");
290+
lineNumber = (String)locatedNode.getUserData("lineNumber");
281291
}
282292
} catch (Exception e) {
283293
logger.error("Unable to locate line for expression [{}] [{}]: {}", xpathExpression, xpathExpressionConverted, e.getMessage());
284-
return "0";
294+
lineNumber = "0";
295+
}
296+
return new LocationInfo(toPathForPresentation(xpathExpression), lineNumber);
297+
}
298+
299+
/**
300+
* Concert the provided XPath expression to one to be used for reporting.
301+
*
302+
* @param xpathExpression The XPath expression to process.
303+
* @return The location path to use.
304+
*/
305+
private String toPathForPresentation(String xpathExpression) {
306+
if (xpathExpression != null) {
307+
return xpathExpression
308+
.replaceAll("\\*:", "")
309+
.replaceAll("\\[\\s*namespace-uri\\(\\)\\s*=\\s*(?:'[^\\[\\]]+'|\"[^\\[\\]]+\")\\s*]", "");
310+
} else {
311+
return null;
285312
}
286313
}
287314

@@ -369,4 +396,12 @@ private boolean documentHasDefaultNamespace(Document node) {
369396
return hasDefaultNamespace;
370397
}
371398

399+
/**
400+
* Record to group together the XPath path and line number to represent a location.
401+
*
402+
* @param path The XPath expression.
403+
* @param lineNumber The line number.
404+
*/
405+
private record LocationInfo(String path, String lineNumber) {}
406+
372407
}

xmlvalidator-common/src/main/java/eu/europa/ec/itb/xml/validation/ValidationConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class ValidationConstants {
3030
public static final String INPUT_LOCATION_AS_PATH = "locationAsPath";
3131
/** Whether the validated content should be added to the TAR report. */
3232
public static final String INPUT_ADD_INPUT_TO_REPORT = "addInputToReport";
33+
/** Whether a simplified XPath expression should be added to report item locations. */
34+
public static final String INPUT_SHOW_LOCATION_PATHS = "showLocationPaths";
3335
/** The locale string to consider. */
3436
public static final String INPUT_LOCALE = "locale";
3537

xmlvalidator-common/src/main/java/eu/europa/ec/itb/xml/validation/XMLValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ private TAR validateSchematron(File schematronFile, boolean supportPureValidatio
481481
} catch (Exception e) {
482482
throw new IllegalStateException("Schematron file ["+schematronFile.getName()+"] is invalid", e);
483483
}
484-
SchematronReportHandler handler = new SchematronReportHandler(specs.inputAsDocumentForSchematronValidation(), svrlOutput, convertXPathExpressions, specs.getDomainConfig().isIncludeTestDefinition(), specs.getDomainConfig().isIncludeAssertionID(), specs.isLocationAsPath(), specs.getLocalisationHelper());
484+
SchematronReportHandler handler = new SchematronReportHandler(specs.inputAsDocumentForSchematronValidation(), svrlOutput, convertXPathExpressions, specs.getDomainConfig().isIncludeTestDefinition(), specs.getDomainConfig().isIncludeAssertionID(), specs.isLocationAsPath(), specs.isShowLocationPaths(), specs.getLocalisationHelper());
485485
return handler.createReport();
486486
}
487487

xmlvalidator-common/src/main/resources/application.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ validator.defaultContextFilesDescription=A list of maps that defines context fil
5555
validator.defaultLocationAsPathDescription=Whether error locations should be XPath expressions or resolve their line and column locations in the provided input.
5656
validator.defaultLocaleDescription=Locale (language code) to use for reporting of results. If the provided locale is not supported by the validator the default locale will be used instead (e.g. "fr", "fr_FR").
5757
validator.defaultAddInputToReportDescription=Whether the returned XML validation report should also include the validated input as context information.
58+
validator.defaultShowLocationPathsDescription=Whether report items should also include a simplified XPath expression as part of their location.
5859
# Properties for the OpenAPI/Swagger documentation.
5960
springdoc.packagesToScan = eu.europa.ec.itb.xml.rest
6061
springdoc.pathsToMatch = /**

xmlvalidator-ws/src/main/java/eu/europa/ec/itb/xml/rest/RestValidationController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ private TAR executeValidationProcess(Input in, DomainConfig domainConfig) {
182182
var validationType = inputHelper.validateValidationType(domainConfig, in.getValidationType());
183183
var locationAsPath = Objects.requireNonNullElse(in.getLocationAsPath(), true);
184184
var addInputToReport = Objects.requireNonNullElse(in.getAddInputToReport(), false);
185+
var showLocationPaths = Objects.requireNonNullElse(in.getShowLocationPaths(), domainConfig.isIncludeLocationPath());
185186
var contentEmbeddingMethod = inputHelper.getEmbeddingMethod(in.getEmbeddingMethod());
186187
var externalSchemas = getExternalSchemas(domainConfig, in.getExternalSchemas(), validationType, DomainConfig.ARTIFACT_TYPE_SCHEMA, parentFolder);
187188
var externalSchematrons = getExternalSchemas(domainConfig, in.getExternalSchematrons(), validationType, DomainConfig.ARTIFACT_TYPE_SCHEMATRON, parentFolder);
@@ -194,6 +195,7 @@ private TAR executeValidationProcess(Input in, DomainConfig domainConfig) {
194195
.withExternalSchematrons(externalSchematrons)
195196
.locationAsPath(locationAsPath)
196197
.addInputToReport(addInputToReport)
198+
.showLocationPaths(showLocationPaths)
197199
.withContextFiles(contextFiles)
198200
.withTempFolder(parentFolder.toPath())
199201
.build();

xmlvalidator-ws/src/main/java/eu/europa/ec/itb/xml/rest/model/Input.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class Input {
3030
private Boolean locationAsPath;
3131
@Schema(description = "Whether to include the validated input in the resulting report's context section.", defaultValue = "false")
3232
private Boolean addInputToReport;
33+
@Schema(description = "Whether report items should also include a simplified XPath expression as part of their location.", defaultValue = "false")
34+
private Boolean showLocationPaths;
3335
@Schema(description = "Whether to wrap the input (see addInputToReport) in a CDATA block if producing an XML report. False results in adding the input via XML escaping.", defaultValue = "false")
3436
private Boolean wrapReportDataInCDATA;
3537
@Schema(description = "Locale (language code) to use for reporting of results. If the provided locale is not supported by the validator the default locale will be used instead (e.g. 'fr', 'fr_FR').")
@@ -138,6 +140,20 @@ public void setAddInputToReport(Boolean addInputToReport) {
138140
this.addInputToReport = addInputToReport;
139141
}
140142

143+
/**
144+
* @return Whether report items should also include a simplified XPath expression as part of their location.
145+
*/
146+
public Boolean getShowLocationPaths() {
147+
return showLocationPaths;
148+
}
149+
150+
/**
151+
* @param showLocationPaths Whether report items should also include a simplified XPath expression as part of their location.
152+
*/
153+
public void setShowLocationPaths(Boolean showLocationPaths) {
154+
this.showLocationPaths = showLocationPaths;
155+
}
156+
141157
/**
142158
* @return Whether to wrap the input (see addInputToReport) in a CDATA block if producing an XML report. False results in adding the input via XML escaping.
143159
*/

0 commit comments

Comments
 (0)