Skip to content

Commit 7d743a3

Browse files
committed
Display an informative error message when a SCH file needs conversion to XSLT before being used
1 parent 3fda701 commit 7d743a3

2 files changed

Lines changed: 70 additions & 7 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (C) 2025 European Union
3+
*
4+
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent
5+
* versions of the EUPL (the "Licence"); You may not use this work except in compliance with the Licence.
6+
*
7+
* You may obtain a copy of the Licence at:
8+
*
9+
* https://interoperable-europe.ec.europa.eu/collection/eupl/eupl-text-eupl-12
10+
*
11+
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is distributed on an
12+
* "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licence for
13+
* the specific language governing permissions and limitations under the Licence.
14+
*/
15+
16+
package eu.europa.ec.itb.xml.validation;
17+
18+
import com.helger.commons.error.IError;
19+
import com.helger.schematron.pure.errorhandler.LoggingPSErrorHandler;
20+
21+
import javax.annotation.Nonnull;
22+
23+
/**
24+
* Custom error handler that is used in the case of pure schematron resources to detect whether errors are due to
25+
* the presence of external functions.
26+
* <p/>
27+
* In this case, the pure Schematron file should first be converted to XSLT format to avoid doing this on the fly for
28+
* each validation.
29+
*/
30+
public class PureSchematronErrorHandler extends LoggingPSErrorHandler {
31+
32+
private boolean dueToExternalFunctionCall = false;
33+
34+
@Override
35+
protected void handleInternally(@Nonnull IError aError) {
36+
super.handleInternally(aError);
37+
if (!dueToExternalFunctionCall && aError.getLinkedException() != null && aError.getLinkedException().getMessage() != null) {
38+
dueToExternalFunctionCall = aError.getLinkedException().getMessage().contains("External function calls have been disabled");
39+
}
40+
}
41+
42+
public boolean isDueToExternalFunctionCall() {
43+
return dueToExternalFunctionCall;
44+
}
45+
46+
}

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -473,28 +473,45 @@ private SchematronOutputType applySchematron(ISchematronResource schematron) thr
473473
private TAR validateSchematron(File schematronFile, boolean supportPureValidationApproach) {
474474
SchematronOutputType svrlOutput;
475475
boolean convertXPathExpressions = false;
476+
boolean retryAsXslt = false;
476477
String schematronFileName = schematronFile.getName().toLowerCase();
478+
ISchematronResource schematronResource = null;
477479
try {
478480
if (schematronFileName.endsWith("xslt") || schematronFileName.endsWith("xsl")) {
479481
// Validate as XSLT.
480-
svrlOutput = applySchematron(schematronAsXSLT(schematronFile));
482+
schematronResource = schematronAsXSLT(schematronFile);
481483
} else if (schematronFileName.endsWith("sch")) {
482484
// Validate as raw schematron.
483485
convertXPathExpressions = supportPureValidationApproach;
484-
svrlOutput = applySchematron(schematronAsRaw(schematronFile, supportPureValidationApproach));
486+
schematronResource = schematronAsRaw(schematronFile, supportPureValidationApproach);
485487
} else {
486488
// We're not certain - validate as raw and if that fails validate as XSLT.
487-
try {
488-
convertXPathExpressions = supportPureValidationApproach;
489-
svrlOutput = applySchematron(schematronAsRaw(schematronFile, supportPureValidationApproach));
490-
} catch (Exception e) {
489+
convertXPathExpressions = supportPureValidationApproach;
490+
schematronResource = schematronAsRaw(schematronFile, supportPureValidationApproach);
491+
retryAsXslt = true;
492+
}
493+
try {
494+
if (schematronResource instanceof SchematronResourcePure pureSchematron) {
495+
pureSchematron.setErrorHandler(new PureSchematronErrorHandler());
496+
}
497+
svrlOutput = applySchematron(schematronResource);
498+
} catch (Exception e) {
499+
if (retryAsXslt) {
491500
// Try also as XSLT.
492501
convertXPathExpressions = false;
493502
svrlOutput = applySchematron(schematronAsXSLT(schematronFile));
503+
} else {
504+
throw e;
494505
}
495506
}
496507
} catch (Exception e) {
497-
throw new IllegalStateException("Schematron file ["+schematronFile.getName()+"] is invalid", e);
508+
if (schematronResource instanceof SchematronResourcePure pureSchematron
509+
&& pureSchematron.getErrorHandler() instanceof PureSchematronErrorHandler errorHandler
510+
&& errorHandler.isDueToExternalFunctionCall()) {
511+
throw new IllegalStateException("Schematron file ["+schematronFile.getName()+"] is provided in pure Schematron format (as a .sch file) and contains references to functions (built-in or external). To be able to use functions you must convert the Schematron file to its XSLT representation and use the XSLT file instead", e);
512+
} else {
513+
throw new IllegalStateException("Schematron file ["+schematronFile.getName()+"] is invalid", e);
514+
}
498515
}
499516
SchematronReportHandler handler = new SchematronReportHandler(specs.inputAsDocumentForSchematronValidation(), svrlOutput, convertXPathExpressions, specs.getDomainConfig().isIncludeTestDefinition(), specs.getDomainConfig().isIncludeAssertionID(), specs.isLocationAsPath(), specs.isShowLocationPaths(), specs.getLocalisationHelper());
500517
return handler.createReport();

0 commit comments

Comments
 (0)