diff --git a/README.md b/README.md index 3e2cf6ad7..5eaea5e7b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Continue reading the **full documentation** at http://phax.github.io/ph-schematr * Improved support for `base-uri()` XPath function when using the pure implementation (#47) * Fixed issue with `role` attribute in SVRL when using pure implementation (#54) * Updated to Saxon-HE 9.8.0-6 + * Added ANT task property `failOnError` (#57) * v4.3.4 - 2017-07-27 * Added new class `SchematronDebug` that centrally manages the debug flags for logging etc. * v4.3.3 - 2017-07-27 @@ -211,6 +212,7 @@ The `schematron` element allows for the following attributes: * `String` **phaseName** - The optional Schematron phase to be used. Note: this is only available when using the processing engine `pure` or `schematron`. For engine `xslt` this is not available because this was defined when the XSLT was created. * `String` **languageCode** - The optional language code to be used. Note: this is only available when using the processing engine `schematron`. For engine `xslt` this is not available because this was defined when the XSLT was created. Default is English (en). Supported language codes are: cs, de, en, fr, nl. * `boolean` **expectSuccess** - `true` to expect successful validation, `false` to expect validation errors. If the expectation is incorrect, the build will fail. + * `boolean` **failOnError** (since v5.0.0) - `true` to break the build if an error occurred, `false` to continue with the following tasks on error. Additionally you can use an `XMLCatalog` that acts as an Entity and URI resolver both for the Schematron and the XML files to be validated! See https://ant.apache.org/manual/Types/xmlcatalog.html for details on the XML catalog. Here is an example that shows how to use an inline XML catalog: @@ -263,6 +265,7 @@ The `schematron` element allows for the following attributes: * `boolean` **keepDiagnostics** - `true` to keep `<diagnostic>`-elements, `false` to delete them. Default is `false`. * `boolean` **keepReports** - `true` to keep `<report>`-elements, `false` to change them to `<assert>`-elements. Default is `false`. * `boolean` **keepEmptyPatterns** - `true` to keep `<pattern>`-elements without rules, `false` to delete them. Default is `true`. + * `boolean` **failOnError** - `true` to break the build if an error occurred, `false` to continue with the following tasks on error. --- diff --git a/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/Schematron.java b/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/Schematron.java index ac547eef4..4af12450e 100644 --- a/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/Schematron.java +++ b/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/Schematron.java @@ -111,10 +111,16 @@ public class Schematron extends Task /** * true if the XMLs are supposed to be valid, false - * otherwise. + * otherwise. Defaults to true. */ private boolean m_bExpectSuccess = true; + /** + * true if the build should fail if any error occurs. Defaults to + * true. Since v5.0.0. + */ + private boolean m_bFailOnError = true; + /** * For resolving entities such as DTDs. This is used both for the Schematron * file as well as for the XML files to be validated. @@ -188,6 +194,13 @@ public void setExpectSuccess (final boolean bExpectSuccess) Project.MSG_DEBUG); } + public void setFailOnError (final boolean bFailOnError) + { + m_bFailOnError = bFailOnError; + + log (bFailOnError ? "Will fail on error" : "Will not fail on error", Project.MSG_DEBUG); + } + /** * Add the catalog to our internal catalog * @@ -238,6 +251,18 @@ private static File _getKeyFile (final File f) return f != null ? f : NULL_FILE_PLACEHOLDER; } + private void _buildError (@Nonnull final String sMsg) + { + _buildError (sMsg, null); + } + + private void _buildError (@Nonnull final String sMsg, @Nullable final Throwable t) + { + if (m_bFailOnError) + throw new BuildException (sMsg, t); + log (sMsg, t, Project.MSG_ERR); + } + private void _performValidation (@Nonnull final ISchematronResource aSch, @Nonnull final ICommonsList aResCollections, @Nullable final File aSVRLDirectory, @@ -248,35 +273,38 @@ private void _performValidation (@Nonnull final ISchematronResource aSch, for (final ResourceCollection aResCollection : aResCollections) { if (!aResCollection.isFilesystemOnly ()) - throw new BuildException ("Only FileSystem resources are supported."); - - for (final Resource aRes : aResCollection) - { - if (!aRes.isExists ()) - throw new BuildException ("Could not find resource " + aRes.toLongString () + " to copy."); - - File baseDir = NULL_FILE_PLACEHOLDER; - String name = aRes.getName (); - final FileProvider fp = aRes.as (FileProvider.class); - if (fp != null) + _buildError ("Only FileSystem resources are supported."); + else + for (final Resource aRes : aResCollection) { - final FileResource fr = ResourceUtils.asFileResource (fp); - baseDir = _getKeyFile (fr.getBaseDir ()); - if (baseDir == NULL_FILE_PLACEHOLDER) - name = fr.getFile ().getAbsolutePath (); - } + if (!aRes.isExists ()) + { + _buildError ("Could not find resource " + aRes.toLongString () + " to copy."); + continue; + } - if ((aRes.isDirectory () || fp != null) && name != null) - { - final DirectoryData aBaseDir = aFiles.computeIfAbsent (_getKeyFile (baseDir), k -> new DirectoryData (k)); - if (aRes.isDirectory ()) - aBaseDir.addDir (name); + File baseDir = NULL_FILE_PLACEHOLDER; + String name = aRes.getName (); + final FileProvider fp = aRes.as (FileProvider.class); + if (fp != null) + { + final FileResource fr = ResourceUtils.asFileResource (fp); + baseDir = _getKeyFile (fr.getBaseDir ()); + if (baseDir == NULL_FILE_PLACEHOLDER) + name = fr.getFile ().getAbsolutePath (); + } + + if ((aRes.isDirectory () || fp != null) && name != null) + { + final DirectoryData aBaseDir = aFiles.computeIfAbsent (_getKeyFile (baseDir), k -> new DirectoryData (k)); + if (aRes.isDirectory ()) + aBaseDir.addDir (name); + else + aBaseDir.addFile (name); + } else - aBaseDir.addFile (name); + _buildError ("Could not resolve resource " + aRes.toLongString () + " to a file."); } - else - throw new BuildException ("Could not resolve resource " + aRes.toLongString () + " to a file."); - } } for (final DirectoryData aBaseDir : aFiles.values ()) @@ -350,10 +378,12 @@ private void _performValidation (@Nonnull final ISchematronResource aSch, for (final AbstractSVRLMessage aMsg : aMessages) { - log (ErrorTextProvider.DEFAULT.getErrorText (aMsg.getAsResourceError (aXMLFile.getPath ()), Locale.US), + log (ErrorTextProvider.DEFAULT.getErrorText (aMsg.getAsResourceError (aXMLFile.getPath ()), + Locale.US), aMsg.getFlag ().isError () ? Project.MSG_ERR : Project.MSG_WARN); } - throw new BuildException (sMessage); + _buildError (sMessage); + continue; } // Success as expected @@ -375,7 +405,8 @@ private void _performValidation (@Nonnull final ISchematronResource aSch, sMessage += " - only " + sWarnings + " are contained"; log (sMessage, Project.MSG_ERR); - throw new BuildException (sMessage); + _buildError (sMessage); + continue; } // Success as expected @@ -405,7 +436,8 @@ private void _performValidation (@Nonnull final ISchematronResource aSch, " - " + ex.getMessage (); log (sMessage, ex, Project.MSG_DEBUG); - throw new BuildException (sMessage, ex); + _buildError (sMessage, ex); + continue; } } } @@ -415,98 +447,111 @@ private void _performValidation (@Nonnull final ISchematronResource aSch, @Override public void execute () throws BuildException { + boolean bCanRun = false; if (m_aSchematronFile == null) - throw new BuildException ("No Schematron file specified!"); - if (m_aSchematronFile.exists () && !m_aSchematronFile.isFile ()) - throw new BuildException ("The specified Schematron file " + m_aSchematronFile + " is not a file!"); - if (m_eSchematronProcessingEngine == null) - throw new BuildException ("An invalid Schematron processing instance is specified! Only one of the following values is allowed: " + - StringHelper.getImplodedMapped (", ", - ESchematronMode.values (), - x -> "'" + x.getID () + "'")); - if (m_aResCollections.isEmpty ()) - throw new BuildException ("No XML resources to be validated specified! Add e.g. a element."); - - if (m_aSvrlDirectory != null) - { - if (!m_aSvrlDirectory.exists () && !m_aSvrlDirectory.mkdirs ()) - throw new BuildException ("Failed to create the SVRL directory " + m_aSvrlDirectory); - } + _buildError ("No Schematron file specified!"); + else + if (m_aSchematronFile.exists () && !m_aSchematronFile.isFile ()) + _buildError ("The specified Schematron file " + m_aSchematronFile + " is not a file!"); + else + if (m_eSchematronProcessingEngine == null) + _buildError ("An invalid Schematron processing instance is specified! Only one of the following values is allowed: " + + StringHelper.getImplodedMapped (", ", ESchematronMode.values (), x -> "'" + x.getID () + "'")); + else + if (m_aResCollections.isEmpty ()) + _buildError ("No XML resources to be validated specified! Add e.g. a element."); + else + if (m_aSvrlDirectory != null) + { + if (!m_aSvrlDirectory.exists () && !m_aSvrlDirectory.mkdirs ()) + _buildError ("Failed to create the SVRL directory " + m_aSvrlDirectory); + } + else + bCanRun = true; - // 1. Parse Schematron file - final Locale aDisplayLocale = Locale.US; - ISchematronResource aSch; - IErrorList aSCHErrors; - switch (m_eSchematronProcessingEngine) + if (bCanRun) { - case PURE: - { - // pure - final CollectingPSErrorHandler aErrorHdl = new CollectingPSErrorHandler (); - final SchematronResourcePure aRealSCH = new SchematronResourcePure (new FileSystemResource (m_aSchematronFile)); - aRealSCH.setPhase (m_sPhaseName); - aRealSCH.setErrorHandler (aErrorHdl); - aRealSCH.setEntityResolver (getEntityResolver ()); - aRealSCH.validateCompletely (); - - aSch = aRealSCH; - aSCHErrors = aErrorHdl.getAllErrors (); - break; - } - case SCHEMATRON: + // 1. Parse Schematron file + final Locale aDisplayLocale = Locale.US; + ISchematronResource aSch = null; + IErrorList aSCHErrors = null; + switch (m_eSchematronProcessingEngine) { - // SCH - final CollectingTransformErrorListener aErrorHdl = new CollectingTransformErrorListener (); - final SchematronResourceSCH aRealSCH = new SchematronResourceSCH (new FileSystemResource (m_aSchematronFile)); - aRealSCH.setPhase (m_sPhaseName); - aRealSCH.setLanguageCode (m_sLanguageCode); - aRealSCH.setErrorListener (aErrorHdl); - aRealSCH.setURIResolver (getURIResolver ()); - aRealSCH.setEntityResolver (getEntityResolver ()); - aRealSCH.isValidSchematron (); - - aSch = aRealSCH; - aSCHErrors = aErrorHdl.getErrorList (); - break; + case PURE: + { + // pure + final CollectingPSErrorHandler aErrorHdl = new CollectingPSErrorHandler (); + final SchematronResourcePure aRealSCH = new SchematronResourcePure (new FileSystemResource (m_aSchematronFile)); + aRealSCH.setPhase (m_sPhaseName); + aRealSCH.setErrorHandler (aErrorHdl); + aRealSCH.setEntityResolver (getEntityResolver ()); + aRealSCH.validateCompletely (); + + aSch = aRealSCH; + aSCHErrors = aErrorHdl.getAllErrors (); + break; + } + case SCHEMATRON: + { + // SCH + final CollectingTransformErrorListener aErrorHdl = new CollectingTransformErrorListener (); + final SchematronResourceSCH aRealSCH = new SchematronResourceSCH (new FileSystemResource (m_aSchematronFile)); + aRealSCH.setPhase (m_sPhaseName); + aRealSCH.setLanguageCode (m_sLanguageCode); + aRealSCH.setErrorListener (aErrorHdl); + aRealSCH.setURIResolver (getURIResolver ()); + aRealSCH.setEntityResolver (getEntityResolver ()); + aRealSCH.isValidSchematron (); + + aSch = aRealSCH; + aSCHErrors = aErrorHdl.getErrorList (); + break; + } + case XSLT: + { + // SCH + final CollectingTransformErrorListener aErrorHdl = new CollectingTransformErrorListener (); + final SchematronResourceXSLT aRealSCH = new SchematronResourceXSLT (new FileSystemResource (m_aSchematronFile)); + // phase and language are ignored because this was decided when the + // XSLT + // was created + aRealSCH.setErrorListener (aErrorHdl); + aRealSCH.setURIResolver (getURIResolver ()); + aRealSCH.setEntityResolver (getEntityResolver ()); + aRealSCH.isValidSchematron (); + + aSch = aRealSCH; + aSCHErrors = aErrorHdl.getErrorList (); + break; + } + default: + _buildError ("No handler for processing engine '" + m_eSchematronProcessingEngine + "'"); + break; } - case XSLT: + if (aSCHErrors != null) { - // SCH - final CollectingTransformErrorListener aErrorHdl = new CollectingTransformErrorListener (); - final SchematronResourceXSLT aRealSCH = new SchematronResourceXSLT (new FileSystemResource (m_aSchematronFile)); - // phase and language are ignored because this was decided when the XSLT - // was created - aRealSCH.setErrorListener (aErrorHdl); - aRealSCH.setURIResolver (getURIResolver ()); - aRealSCH.setEntityResolver (getEntityResolver ()); - aRealSCH.isValidSchematron (); - - aSch = aRealSCH; - aSCHErrors = aErrorHdl.getErrorList (); - break; - } - default: - throw new BuildException ("No handler for processing engine '" + m_eSchematronProcessingEngine + "'"); - } - if (aSCHErrors != null) - { - // Error validating the Schematrons!! - boolean bAnyError = false; - for (final IError aError : aSCHErrors) - if (aError.getErrorLevel ().isGE (EErrorLevel.ERROR)) + // Error validating the Schematrons!! + boolean bAnyParsingError = false; + for (final IError aError : aSCHErrors) + if (aError.getErrorLevel ().isGE (EErrorLevel.ERROR)) + { + log ("Error in Schematron: " + aError.getAsString (aDisplayLocale), Project.MSG_ERR); + bAnyParsingError = true; + } + else + if (aError.getErrorLevel ().isGE (EErrorLevel.WARN)) + log ("Warning in Schematron: " + aError.getAsString (aDisplayLocale), Project.MSG_WARN); + + if (bAnyParsingError) + _buildError ("The provided Schematron file contains errors. See log for details."); + else { - log ("Error in Schematron: " + aError.getAsString (aDisplayLocale), Project.MSG_ERR); - bAnyError = true; + log ("Successfully parsed Schematron file '" + m_aSchematronFile.getPath () + "'", Project.MSG_INFO); + + // 2. for all XML files that match the pattern + _performValidation (aSch, m_aResCollections, m_aSvrlDirectory, m_bExpectSuccess); } - else - if (aError.getErrorLevel ().isGE (EErrorLevel.WARN)) - log ("Warning in Schematron: " + aError.getAsString (aDisplayLocale), Project.MSG_WARN); - if (bAnyError) - throw new BuildException ("The provided Schematron file contains errors. See log for details."); + } } - log ("Successfully parsed Schematron file '" + m_aSchematronFile.getPath () + "'", Project.MSG_INFO); - - // 2. for all XML files that match the pattern - _performValidation (aSch, m_aResCollections, m_aSvrlDirectory, m_bExpectSuccess); } } diff --git a/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/SchematronPreprocess.java b/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/SchematronPreprocess.java index f45053bf2..3fcfbd44e 100644 --- a/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/SchematronPreprocess.java +++ b/ph-schematron-ant-task/src/main/java/com/helger/schematron/ant/SchematronPreprocess.java @@ -19,6 +19,7 @@ import java.io.File; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -79,6 +80,12 @@ public class SchematronPreprocess extends Task */ private boolean m_bKeepEmptyPatterns = PSPreprocessor.DEFAULT_KEEP_EMPTY_PATTERNS; + /** + * true if the build should fail if any error occurs. Defaults to + * true. + */ + private boolean m_bFailOnError = true; + public SchematronPreprocess () {} @@ -124,42 +131,68 @@ public void setKeepEmptyPatterns (final boolean bKeepEmptyPatterns) Project.MSG_DEBUG); } + public void setFailOnError (final boolean bFailOnError) + { + m_bFailOnError = bFailOnError; + + log (bFailOnError ? "Will fail on error" : "Will not fail on error", Project.MSG_DEBUG); + } + + private void _buildError (@Nonnull final String sMsg) + { + _buildError (sMsg, null); + } + + private void _buildError (@Nonnull final String sMsg, @Nullable final Throwable t) + { + if (m_bFailOnError) + throw new BuildException (sMsg, t); + log (sMsg, t, Project.MSG_ERR); + } + @Override public void execute () throws BuildException { + boolean bCanRun = false; if (m_aSrcFile == null) - throw new BuildException ("No source Schematron file specified!"); - if (m_aSrcFile.exists () && !m_aSrcFile.isFile ()) - throw new BuildException ("The specified source Schematron file " + m_aSrcFile + " is not a file!"); - if (m_aDstFile == null) - throw new BuildException ("No destination Schematron file specified!"); - if (m_aDstFile.exists () && !m_aDstFile.isFile ()) - throw new BuildException ("The specified destination Schematron file " + m_aDstFile + " is not a file!"); - - try - { - // Read source - final PSSchema aSchema = new PSReader (new FileSystemResource (m_aSrcFile)).readSchema (); - - // Setup preprocessor - final PSPreprocessor aPreprocessor = new PSPreprocessor (PSXPathQueryBinding.getInstance ()); - aPreprocessor.setKeepTitles (m_bKeepTitles); - aPreprocessor.setKeepDiagnostics (m_bKeepDiagnostics); - aPreprocessor.setKeepReports (m_bKeepReports); - aPreprocessor.setKeepEmptyPatterns (m_bKeepEmptyPatterns); - aPreprocessor.setKeepEmptySchema (true); - - // Main pre-processing - final PSSchema aPreprocessedSchema = aPreprocessor.getAsPreprocessedSchema (aSchema); - - // Write the result file - new PSWriter (new PSWriterSettings ().setXMLWriterSettings (new XMLWriterSettings ())).writeToFile (aPreprocessedSchema, - m_aDstFile); - log ("Successfully pre-processed Schematron " + m_aSrcFile + " to " + m_aDstFile); - } - catch (final SchematronReadException | SchematronPreprocessException ex) - { - throw new BuildException (ex); - } + _buildError ("No source Schematron file specified!"); + else + if (m_aSrcFile.exists () && !m_aSrcFile.isFile ()) + _buildError ("The specified source Schematron file " + m_aSrcFile + " is not a file!"); + else + if (m_aDstFile == null) + _buildError ("No destination Schematron file specified!"); + else + if (m_aDstFile.exists () && !m_aDstFile.isFile ()) + _buildError ("The specified destination Schematron file " + m_aDstFile + " is not a file!"); + else + bCanRun = true; + + if (bCanRun) + try + { + // Read source + final PSSchema aSchema = new PSReader (new FileSystemResource (m_aSrcFile)).readSchema (); + + // Setup preprocessor + final PSPreprocessor aPreprocessor = new PSPreprocessor (PSXPathQueryBinding.getInstance ()); + aPreprocessor.setKeepTitles (m_bKeepTitles); + aPreprocessor.setKeepDiagnostics (m_bKeepDiagnostics); + aPreprocessor.setKeepReports (m_bKeepReports); + aPreprocessor.setKeepEmptyPatterns (m_bKeepEmptyPatterns); + aPreprocessor.setKeepEmptySchema (true); + + // Main pre-processing + final PSSchema aPreprocessedSchema = aPreprocessor.getAsPreprocessedSchema (aSchema); + + // Write the result file + new PSWriter (new PSWriterSettings ().setXMLWriterSettings (new XMLWriterSettings ())).writeToFile (aPreprocessedSchema, + m_aDstFile); + log ("Successfully pre-processed Schematron " + m_aSrcFile + " to " + m_aDstFile); + } + catch (final SchematronReadException | SchematronPreprocessException ex) + { + _buildError ("Error processing Schemtron " + m_aSrcFile.getAbsolutePath (), ex); + } } }