This is an automated email from the ASF dual-hosted git repository. slawrence pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/daffodil.git
The following commit(s) were added to refs/heads/main by this push: new 081151258 Simplify validation API 081151258 is described below commit 0811512580493aceca4b7b536790d792178f9b90 Author: Steve Lawrence <slawre...@apache.org> AuthorDate: Thu Jul 31 08:57:36 2025 -0400 Simplify validation API The currently validator API supports all the features needed to support our existing and custom validators. However, it does add extra complication, since API users must create a Properties instance, find the ValidatorFactory, use that and the Properties to create a Validator, and provide that Validator to the DataProcessor. Although this provides much flexibility, it is flexibility that API users rarely need. The new API is now `DataProcessor.withValidation(String, URI)` where the String is the name of a validator to use (looked up via SPI), and the URI is either a properties file or a file that the Validator can use for validation (e.g. XSD for "xerces", .sch or XSD with embedded schematron rules for "schematron"). This new function uses those parameters and the existing ValidatorFactory API to create and assign the validator to the DataProcessor. Additional changes include: - The converter in Main.scala no longer creates a validator, instead it just parses the validation option into a `(String, Option[URI])` that is passed to the new withValidation function. This means creating the validator can fail when withValidation is called (e.g. if the validator does not exist) and so the CLI adds new exit codes to handle the possible validation failures. If the optional validator value is not provided, we provide the DFDL schema as the URI. - The CLI was setting the validator twice, when when creating the processor and again when setting variables/debug/etc. This removes the duplicates. - The daffodil.rootSchema property cannot be needed now, since withValidation doesn't know if the provided URI is actually the DFDL schema. Instead, validators now only use properties specific to the validator. - makeValidator can never get a null Properties instance now, so the null check is removed - We now require that validators are found via SPI. Two tests are removed that explicitly tested non-SPI behavior. - Add a missing catch if schematron fails to read the .sch file. Previously this would throw a generic exception caught by scallop when creating the validator, but it really should be caught by the SchematronValidator and returned as an initialization exception. - The TDMLRunner is modified so it only ever knows about "on", "limited" and "off" validation modes. It then passes those unmodified strings directly to the TDMLDFDLProcessor implementation, which must convert the to whatever the implementation uses for validation. The DaffodilTDMLDFDLProcessor now keeps track of the schema URI and uses the new DataProcessor.withValidation, passing in the validation mode (converting "on" to "xerces") and the schema URI. - The validation package-info.java file is changed to focus on the validator names and properties instead of the implementations, since users don't really need to care about ValidatorFactories's or Validators as much with the new API DAFFODIL-3013 --- .../main/scala/org/apache/daffodil/cli/Main.scala | 102 ++++++------ .../daffodil/cli/cliTest/TestCLIParsing.scala | 2 +- .../cli/cliTest/schematron/TestValidating.scala | 4 +- .../org/apache/daffodil/api/DataProcessor.java | 36 ++++- .../apache/daffodil/api/validation/Validator.java | 4 - .../daffodil/api/validation/ValidatorFactory.java | 12 -- .../daffodil/api/validation/package-info.java | 179 +++++++++++---------- .../org/apache/daffodil/core/util/TestUtils.scala | 14 +- .../runtime1/processors/DataProcessor.scala | 26 ++- .../validation/DaffodilLimitedValidator.scala | 4 +- .../apache/daffodil/validation/NoValidator.scala | 4 +- .../daffodil/validation/XercesValidator.scala | 13 +- .../java/org/apache/daffodil/jexample/TestAPI.java | 16 +- .../daffodil/jexample/ValidatorApiExample.java | 8 +- .../daffodil/jexample/ValidatorSpiExample.java | 9 +- .../lib/validation/TestValidatorsSPI.scala | 7 +- .../lib/validation/TestXercesValidator.scala | 9 +- .../org/apache/daffodil/sexample/TestAPI.scala | 43 ++--- .../daffodil/sexample/ValidatorApiExample.scala | 42 ----- .../sexample/ValidatorExamplesSupport.scala | 15 -- .../daffodil/sexample/ValidatorSpiExample.scala | 10 +- .../schematron/SchematronValidatorFactory.scala | 29 ++-- .../validation/schematron/EmbeddedTesting.scala | 8 +- .../org/apache/daffodil/tdml/TDMLRunner.scala | 55 +++---- .../tdml/processor/TDMLDFDLProcessor.scala | 2 +- .../tdml/DaffodilCTDMLDFDLProcessor.scala | 2 +- .../processor/tdml/DaffodilTDMLDFDLProcessor.scala | 36 +++-- .../apache/daffodil/infoset/TestStringAsXml.scala | 12 +- 28 files changed, 301 insertions(+), 402 deletions(-) diff --git a/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala b/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala index 9620497a0..7fe4d24ac 100644 --- a/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala +++ b/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala @@ -29,7 +29,6 @@ import java.nio.channels.FileChannel import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardOpenOption -import java.util.Properties import java.util.Scanner import java.util.concurrent.Executors import javax.xml.parsers.SAXParserFactory @@ -48,8 +47,8 @@ import scala.util.matching.Regex import org.apache.daffodil.api import org.apache.daffodil.api.debugger.InteractiveDebuggerRunnerFactory import org.apache.daffodil.api.exceptions.InvalidParserException -import org.apache.daffodil.api.validation.ValidatorFactory -import org.apache.daffodil.api.validation.Validators +import org.apache.daffodil.api.validation.ValidatorInitializationException +import org.apache.daffodil.api.validation.ValidatorNotRegisteredException import org.apache.daffodil.cli.debugger.CLIDebuggerRunner import org.apache.daffodil.core.compiler.Compiler import org.apache.daffodil.core.dsom.ExpressionCompilers @@ -86,7 +85,6 @@ import org.apache.daffodil.slf4j.DaffodilLogger import org.apache.daffodil.tdml.Runner import org.apache.daffodil.tdml.TDMLException import org.apache.daffodil.tdml.TDMLTestNotCompatibleException -import org.apache.daffodil.validation.NoValidator import com.siemens.ct.exi.core.EXIFactory import com.siemens.ct.exi.core.exceptions.EXIException @@ -167,40 +165,27 @@ class CLIConf(arguments: Array[String], stdout: PrintStream, stderr: PrintStream def validateConverter( schema: ScallopOption[URISchemaSource], parser: ScallopOption[File] - ): ValueConverter[api.validation.Validator] = - singleArgConverter[api.validation.Validator]((s: String) => { + ): ValueConverter[(String, Option[URI])] = + singleArgConverter[(String, Option[URI])]((s: String) => { import ValidatorPatterns._ def doesNotSupportReloadableParsers(name: String): Boolean = // TODO: DAFFODIL-1749, this will change once ticket is implemented schema.isEmpty && parser.isDefined && !Seq("limited", "off").exists(_.contains(name)) - val defaults = { - schema - .map(sch => ValidatorFactory.makeConfig(sch.uri.toString)) - .getOrElse(new Properties()) - } - val props = new Properties(defaults) - s match { - case DefaultArgPattern(name, arg) if Validators.isRegistered(name) => + case DefaultArgPattern(name, arg) => if (doesNotSupportReloadableParsers(name)) { - null + null // validateOpt will cause null to output an appropriate error message } else { - if (Seq(".conf", ".properties").exists(arg.endsWith)) { - val is = new FileInputStream(arg) - props.load(is) - } else { - val uss = fileResourceToURI(arg) - props.setProperty(name, uss.uri.toString) - } - Validators.get(name).make(props) + val uri = fileResourceToURI(arg).uri + (name, Some(uri)) } - case NoArgsPattern(name) if Validators.isRegistered(name) => + case NoArgsPattern(name) => if (doesNotSupportReloadableParsers(name)) { - null + null // validateOpt will cause null to output an appropriate error message } else { - Validators.get(name).make(props) + (name, schema.map(_.uri).toOption) } case _ => throw new Exception( @@ -436,14 +421,14 @@ class CLIConf(arguments: Array[String], stdout: PrintStream, stderr: PrintStream descr = "Tunable configuration options to change Daffodil's behavior. " + "Only valid with the --schema option." ) - val validate: ScallopOption[api.validation.Validator] = opt[api.validation.Validator]( + val validate: ScallopOption[(String, Option[URI])] = opt[(String, Option[URI])]( short = 'V', - default = Some(NoValidator), + default = Some(("off", None)), argName = "validator_name", descr = - "Validator name. Use 'xerces=[value]', 'limited', 'off', or a validator_plugin_name=value, where value" + - "is an optional schema to validate against, a key/value configuration file or a required schematron" + - " schema in the case of the schematron validator plugin." + "Validator name. Use 'off', 'limited', 'xerces[=value]', 'schematron[=value]', or a " + + "custom validator_name[=value]. The optional value paramter provides a file to the " + + "validator (e.g. .xsd, .sch, .conf, .properties) used for validator configuration." )(validateConverter(schema, parser)) val debug = opt[Option[String]]( argName = "file", @@ -566,14 +551,14 @@ class CLIConf(arguments: Array[String], stdout: PrintStream, stderr: PrintStream descr = "Tunable configuration options to change Daffodil's behavior. " + "Only valid with the --schema option." ) - val validate: ScallopOption[api.validation.Validator] = opt[api.validation.Validator]( + val validate: ScallopOption[(String, Option[URI])] = opt[(String, Option[URI])]( short = 'V', - default = Some(NoValidator), + default = Some(("off", None)), argName = "validator_name", descr = - "Validator name. Use 'xerces=[value]', 'limited', 'off', or a validator_plugin_name=value, where value" + - "is an optional schema to validate against, a key/value configuration file or a required schematron" + - " schema in the case of the schematron validator plugin." + "Validator name. Use 'off', 'limited', 'xerces[=value]', 'schematron[=value]', or a " + + "custom validator_name[=value]. The optional value paramter provides a file to the " + + "validator (e.g. .xsd, .sch, .conf, .properties) used for validator configuration." )(validateConverter(schema, parser)) val debug = opt[Option[String]]( argName = "file", @@ -801,14 +786,14 @@ class CLIConf(arguments: Array[String], stdout: PrintStream, stderr: PrintStream default = Some(false), descr = "Perform unparse instead of parse for performance test" ) - val validate: ScallopOption[api.validation.Validator] = opt[api.validation.Validator]( + val validate: ScallopOption[(String, Option[URI])] = opt[(String, Option[URI])]( short = 'V', - default = Some(NoValidator), + default = Some(("off", None)), argName = "validator_name", descr = - "Validator name. Use 'xerces=[value]', 'limited', 'off', or a validator_plugin_name=value, where value" + - "is an optional schema to validate against, a key/value configuration file or a required schematron" + - " schema in the case of the schematron validator plugin." + "Validator name. Use 'off', 'limited', 'xerces[=value]', 'schematron[=value]', or a " + + "custom validator_name[=value]. The optional value paramter provides a file to the " + + "validator (e.g. .xsd, .sch, .conf, .properties) used for validator configuration." )(validateConverter(schema, parser)) val infile = trailArg[String]( @@ -990,6 +975,7 @@ object Main { val UserDefinedFunctionError = Value(34) val UnableToCreateProcessor = Value(35) val LayerExecutionError = Value(36) + val UnableToCreateValidatorError = Value(37) val Usage = Value(64) } @@ -1070,7 +1056,7 @@ class Main( def createProcessorFromParser( savedParser: File, path: Option[String], - validator: api.validation.Validator + validate: (String, Option[URI]) ) = { try { val compiler = Compiler() @@ -1084,7 +1070,8 @@ class Main( Logger.log.debug(s"Parser = ${processorImpl.ssrd.parser.toString}") Logger.log.debug(s"Unparser = ${processorImpl.ssrd.unparser.toString}") } - Some(processor.withValidator(validator)) + val (validationKind, validationConfig) = validate + Some(processor.withValidation(validationKind, validationConfig.getOrElse(null))) } else { None } @@ -1136,7 +1123,7 @@ class Main( rootNS: Option[RefQName], path: Option[String], tunablesMap: Map[String, String], - validator: api.validation.Validator + validate: (String, Option[URI]) ): Option[api.DataProcessor] = { val compiler = { val c = Compiler().withTunables(tunablesMap) @@ -1157,9 +1144,10 @@ class Main( "compiling", { val processorFactory = compiler.compileSource(schemaSource) if (!processorFactory.isError) { + val (validationKind, validationConfig) = validate val processor = processorFactory .onPath(path.getOrElse("/")) - .withValidator(validator) + .withValidation(validationKind, validationConfig.getOrElse(null)) // needed to access SchemaSetRuntimeData .asInstanceOf[DataProcessor] if (processor.isError) { @@ -1239,7 +1227,7 @@ class Main( case Some(conf.parse) => { val parseOpts = conf.parse - val validate = parseOpts.validate.toOption.get + val validate = parseOpts.validate() val optDafConfig = parseOpts.config.toOption.map { DaffodilConfig.fromFile(_) } @@ -1262,8 +1250,7 @@ class Main( .withExternalVariables( combineExternalVariables(parseOpts.vars, optDafConfig) ) - }.map { _.withValidator(validate) } - .map { withDebugOrTrace(_, parseOpts.trace, parseOpts.debug) } + }.map { withDebugOrTrace(_, parseOpts.trace, parseOpts.debug) } val rc = processor match { case None => ExitCode.UnableToCreateProcessor @@ -1438,7 +1425,7 @@ class Main( case Some(conf.performance) => { val performanceOpts = conf.performance - val validate = performanceOpts.validate.toOption.get + val validate = performanceOpts.validate() val optDafConfig = performanceOpts.config.toOption.map { DaffodilConfig.fromFile(_) } @@ -1463,7 +1450,7 @@ class Main( }.map { p => p.asInstanceOf[DFDL.DataProcessor] .withExternalVariables(combineExternalVariables(performanceOpts.vars, optDafConfig)) - }.map { _.withValidator(validate) } + } val rc: ExitCode.Value = processor match { case None => ExitCode.UnableToCreateProcessor @@ -1592,7 +1579,7 @@ class Main( case Some(conf.unparse) => { val unparseOpts = conf.unparse - val validate = unparseOpts.validate.toOption.get + val validate = unparseOpts.validate() val optDafConfig = unparseOpts.config.toOption.map { DaffodilConfig.fromFile(_) } @@ -1613,8 +1600,7 @@ class Main( }.map { p => p.asInstanceOf[DFDL.DataProcessor] .withExternalVariables(combineExternalVariables(unparseOpts.vars, optDafConfig)) - }.map { _.withValidator(validate) } - .map { withDebugOrTrace(_, unparseOpts.trace, unparseOpts.debug) } + }.map { withDebugOrTrace(_, unparseOpts.trace, unparseOpts.debug) } val output = unparseOpts.output.toOption match { case Some("-") | None => STDOUT @@ -1696,7 +1682,7 @@ class Main( case Some(conf.save) => { val saveOpts = conf.save - val validate = NoValidator + val validate = ("off", None) val optDafConfig = saveOpts.config.toOption.map { DaffodilConfig.fromFile(_) } val tunables = @@ -2083,6 +2069,14 @@ class Main( Logger.log.error(Misc.getSomeMessage(e).get) ExitCode.BadExternalVariable } + case e: ValidatorNotRegisteredException => { + Logger.log.error(Misc.getSomeMessage(e).get) + ExitCode.UnableToCreateValidatorError + } + case e: ValidatorInitializationException => { + Logger.log.error(Misc.getSomeMessage(e).get) + ExitCode.UnableToCreateValidatorError + } case e: NotYetImplementedException => { nyiFound(e) ExitCode.NotYetImplemented diff --git a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala index 15f13afce..e2a0f0eb6 100644 --- a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala +++ b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLIParsing.scala @@ -552,7 +552,7 @@ class TestCLIParsing { runCLI(args"parse --validate FooBar -s $schema -r matrix") { cli => cli.sendLine("0,1,2", inputDone = true) cli.expectErr("FooBar") - }(ExitCode.Usage) + }(ExitCode.UnableToCreateValidatorError) } @Test def test_CLI_Parsing_invalidElementSDE(): Unit = { diff --git a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/schematron/TestValidating.scala b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/schematron/TestValidating.scala index 3920a24e8..5a62cda13 100644 --- a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/schematron/TestValidating.scala +++ b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/schematron/TestValidating.scala @@ -64,8 +64,8 @@ class TestValidating { val input = path("daffodil-cli/src/test/resources/org/apache/daffodil/cli/input/uuid.txt") runCLI(args"""parse --validate schematron=$schematron -s $schema $input""") { cli => - cli.expectErr("Bad arguments") + cli.expectErr("failed to create schematron validator") cli.expectErr("does-no-exist/title-rules.sch") - }(ExitCode.Usage) + }(ExitCode.UnableToCreateValidatorError) } } diff --git a/daffodil-core/src/main/java/org/apache/daffodil/api/DataProcessor.java b/daffodil-core/src/main/java/org/apache/daffodil/api/DataProcessor.java index 53db53e19..8f5c55bde 100644 --- a/daffodil-core/src/main/java/org/apache/daffodil/api/DataProcessor.java +++ b/daffodil-core/src/main/java/org/apache/daffodil/api/DataProcessor.java @@ -26,12 +26,14 @@ import org.apache.daffodil.api.exceptions.ExternalVariableException; import org.apache.daffodil.api.infoset.InfosetInputter; import org.apache.daffodil.api.infoset.InfosetOutputter; import org.apache.daffodil.api.metadata.MetadataHandler; -import org.apache.daffodil.api.validation.Validator; +import org.apache.daffodil.api.validation.ValidatorInitializationException; +import org.apache.daffodil.api.validation.ValidatorNotRegisteredException; import org.apache.daffodil.core.dsom.ExpressionCompilers$; import org.apache.daffodil.runtime1.debugger.InteractiveDebugger; import java.io.File; import java.io.Serializable; +import java.net.URI; import java.nio.channels.WritableByteChannel; import java.util.Map; @@ -82,14 +84,36 @@ public interface DataProcessor extends WithDiagnostics, Serializable { DataProcessor withDebugger(Debugger dbg); /** - * Obtain a new {@link DataProcessor} with a specified validator. + * Obtain a new {@link DataProcessor} with validation that does not require configuration. * - * @param validator Validator which could be NoValidator for off, or DaffodilLimitedValidator - * for limited, XercesValidator for full, SchematronValidator for schemratron, - * or a custom validator that implements {@link Validator} + * @param kind Kind of validation to use. Can be a custom validator name available via the + * {@link org.apache.daffodil.api.validation.ValidatorFactory} SPI or one of the built-in validators + * ("xerces", "limited", "off", "schematron") * @return a new {@link DataProcessor} with a specified validator. + * @throws ValidatorNotRegisteredException if the validator cannot be found + * @throws ValidatorInitializationException if initializing the validator fails */ - DataProcessor withValidator(Validator validator); + default DataProcessor withValidation(String kind) throws ValidatorNotRegisteredException, ValidatorInitializationException { + return withValidation(kind, null); + } + + /** + * Obtain a new {@link DataProcessor} with validation using a URI for configuration. + * + * @param kind Kind of validation to use. Can be a custom validator name available via the + * {@link org.apache.daffodil.api.validation.ValidatorFactory} SPI or one of the built-in validators + * ("xerces", "limited", "off", "schematron") + * @param config Absolute URI to use for validation configuration. If the URI ends with .conf + * or .properties it is treated as a java.util.Properties file that is loaded and + * provided to the validator. Otherwise, the URI is provided as a single property to + * the validator. Can be null if a URI is not known or the validator does not need + * additional configuration--this could cause an exception if a validator requires + * properties. + * @return a new {@link DataProcessor} with a specified validator. + * @throws ValidatorNotRegisteredException if the validator cannot be found + * @throws ValidatorInitializationException if initializing the validator fails + */ + DataProcessor withValidation(String kind, URI config) throws ValidatorNotRegisteredException, ValidatorInitializationException; /** * Obtain a new {@link DataProcessor} with external variables read from a Daffodil configuration file diff --git a/daffodil-core/src/main/java/org/apache/daffodil/api/validation/Validator.java b/daffodil-core/src/main/java/org/apache/daffodil/api/validation/Validator.java index bc5a52197..0d4a3fd28 100644 --- a/daffodil-core/src/main/java/org/apache/daffodil/api/validation/Validator.java +++ b/daffodil-core/src/main/java/org/apache/daffodil/api/validation/Validator.java @@ -33,10 +33,6 @@ import java.io.Serializable; * ValidationHandler */ public interface Validator extends Serializable { - /** - * default schema key for configuration file - */ - String rootSchemaKey = "daffodil.rootSchema"; /** * Used to validate XMLs. Any validation diagnostics will be added to the state diff --git a/daffodil-core/src/main/java/org/apache/daffodil/api/validation/ValidatorFactory.java b/daffodil-core/src/main/java/org/apache/daffodil/api/validation/ValidatorFactory.java index 704d0ef8f..74510ea2e 100644 --- a/daffodil-core/src/main/java/org/apache/daffodil/api/validation/ValidatorFactory.java +++ b/daffodil-core/src/main/java/org/apache/daffodil/api/validation/ValidatorFactory.java @@ -43,16 +43,4 @@ public interface ValidatorFactory extends SimpleNamedLoadableService { * @throws org.apache.daffodil.api.validation.ValidatorInitializationException when initialization fails */ Validator make(Properties config) throws ValidatorInitializationException; - - /** - * Helper utility to create a Properties file with the rootSchemaKey set, which many validators can use - * - * @param defaultSchema String uri to the default validating schema - * @return Properties object with the default root schema set - */ - static Properties makeConfig(String defaultSchema) { - Properties properties = new Properties(); - properties.setProperty(Validator.rootSchemaKey, defaultSchema); - return properties; - } } diff --git a/daffodil-core/src/main/java/org/apache/daffodil/api/validation/package-info.java b/daffodil-core/src/main/java/org/apache/daffodil/api/validation/package-info.java index 2fd7f8d52..8c244a784 100644 --- a/daffodil-core/src/main/java/org/apache/daffodil/api/validation/package-info.java +++ b/daffodil-core/src/main/java/org/apache/daffodil/api/validation/package-info.java @@ -16,97 +16,104 @@ */ /** - * We provide 4 different types of built-in validators for use with - * {@link org.apache.daffodil.api.DataProcessor#withValidator(org.apache.daffodil.api.validation.Validator)}: - * <ul> - * <li>{@link org.apache.daffodil.validation.NoValidator} - for no validation</li> - * <li>{@link org.apache.daffodil.validation.DaffodilLimitedValidator} - for limited/daffodil schema constraints validation</li> - * <li>{@link org.apache.daffodil.validation.XercesValidator} - for full validation using Xerces schema validation</li> - * <li>see org.apache.daffodil.validation.schematron.SchematronValidator - for schematron validation</li> - * </ul> + * Daffodil Validation API package + * * <p> - * It is also possible to create a custom validator by doing the following: + * Daffodil provides a number of built-in validators for use with {@link + * org.apache.daffodil.api.DataProcessor#withValidation(String, URI)}. For each + * built-in validator, the following contains the validator name, a + * description, and validator specific properties. The {@code String} paramter + * should be the name of the validator. If the URI parameter ends in + * <i>.conf</i> or <i>.properties</i> then it is treated as a {@link + * java.util.Properties} file that provides validator properties. Otherwise, + * the URI is set to a property with the same name as the validator. If a + * validator does not require properties, the URI parameter can be set to + * {@code null} or excluded. + * </p> + * + * <dl> + * <dt><span style="font-size: large;"><b>off</b></span></dt> + * <dd> + * <p><b>Description:</b> disable validation</p> + * <p><b>Properties:</b> none</p> + * <p><b>Example:</b></p> + * <pre>{@code + * // explicitly disable validation + * dataProcessor.withValidation("off") + * }</pre> + * </dd> + * + * <dt><span style="font-size: large;"><b>limited</b></span></dt> + * <dd> + * <p><b>Description:</b> XML schema validation using Daffodil</p> + * <p><b>Properties:</b> none</p> + * <p><b>Example:</b></p> + * <pre>{@code + * // enable XML schema validation using Daffodil + * dataProcessor.withValidation("limited") + * }</pre> + * </dd> + * + * <dt><span style="font-size: large;"><b>xerces</b></span></dt> + * <dd> + * <p><b>Description:</b> XML Schema validation using Xerces library</p> + * <p><b>Properties:</b></p> + * <dl> + * <dt>{@code xerces}</dt> + * <dd>absolute URI to a XSD file</dd> + * </dl> + * <p><b>Example:</b></p> + * <pre>{@code + * // enable XML schema validation, setting the "xerces" property to the schema.xsd file + * dataProcessor.withValidation("xerces", URI.create("file:///path/to/schema.xsd")) + * }</pre> + * </dd> + * + * <dt><span style="font-size: large;"><b>schematron</b></span></dt> + * <dd> + * <p><b>Description:</b> schematron validation using Saxon-HE library</p> + * <p><b>Properties:</b></p> + * <dl> + * <dt>{@code schematron}</dt> + * <dd> + * absolute URI to a file containing schematron rules. If the URI + * ends with {@code .sch} then the file is treated as a schematron file. + * Otherwise, it is treated as an XSD file containing embedded + * schematron rules. + * </dd> + * <dt>{@code schematron.svrl.file}</dt> + * <dd> + * absolute URI to a file to write the results of schematron + * validation in SVRL format. This property is not thread safe and + * will overwrite existing files--it is intended primarily for + * debugging with single file validation. + * </dd> + * </dl> + * <p><b>Example:</b></p> + * <pre>{@code + * // enable schematron validation, setting the "schematron" property to the schematron.sch file + * dataProcessor.withValidation("schematron", URI.create("file:///path/to/schematron.sch")) + * + * // use schematron validation, reading the schematron.properties file to set "schematron" or other schematron properties + * dataProcessor.withValidation("schematron", URI.create("file:///path/to/schematron.properties")) + * }</pre> + * </dd> + * </dl> + * + * <h2>Custom Validators</h2> + * <p> + * Daffodil also supports custom validators. To make a custom validator available to Daffodil, follow these steps: * <ol> - * <li>create a custom Validator class that implements the + * <li>Create a custom class that implements the * {@link org.apache.daffodil.api.validation.Validator} interface</li> - * <li>create a custom ValidatorFactory class that implements the - * {@link org.apache.daffodil.api.validation.ValidatorFactory} interface, whose make method - * returns the custom validator from the previous step</li> - * <li>Register the created custom Validator Factory by creating a {@link org.apache.daffodil.api.validation.ValidatorFactory} + * <li>Create a custom class that implements the + * {@link org.apache.daffodil.api.validation.ValidatorFactory} interface, whose {@code make} method + * returns the custom Validator from the previous step</li> + * <li>Register the custom ValidatorFactory by creating a {@link org.apache.daffodil.api.validation.ValidatorFactory} * file in {@code META-INF/services/}, its contents being the fully qualified name of the custom validator factory</li> + * <li>Call the {@link org.apache.daffodil.api.DataProcessor#withValidation(String, URI)} function, providing the name + * of the validator and optional URI.</li> * </ol> - * <p> - * To get an instance of one of the provided validators, one will need: - * <ul> - * <li> access to registered {@link org.apache.daffodil.api.validation.ValidatorFactory}s - * via the {@link org.apache.daffodil.api.validation.Validators#get(java.lang.String)} command</li> - * <li> a {@link java.util.Properties} object with the appropriate keys/values (see below for specifics for each kind of validator)</li> - * <li> the name of the validator (ex: off, limited, xerces or schematron) </li> - * </ul> - * <p> - * To geta specific Validator factory instance, do the below: - * <pre> - * {@code - * org.apache.daffodil.api.validation.ValidatorFactory vf = org.apache.daffodil.api.validation.Validators.get("xerces") - * } - * </pre> - * To get the validator, certain properties are required to be passed into the make function, - * depending on the validator. - * <ul> - * <li>{@link org.apache.daffodil.validation.NoValidator} - * <ul> - * <li>none; empty Properties object or null</li> - * </ul> - * </li> - * <li>{@link org.apache.daffodil.validation.DaffodilLimitedValidator} - * <ul> - * <li>none; empty Properties object or null</li> - * </ul> - * </li> - * <li>{@link org.apache.daffodil.validation.XercesValidator} - * <ul> - * <li>{@code daffodil.rootSchema=schema_file_uri_string}</li> - * <li>{@code xerces=schema_file_uri_string} - if not found, Daffodil will attempt to use - * the above instead. So one or both must be present, but this will always take precedence over - * daffodil.rootSchema if both are present.</li> - * </ul> - * </li> - * <li>org.apache.daffodil.validation.schematron.SchematronValidator - * <ul> - * <li>{@code schematron=uri_string_to_schematron_file} - if not found, Daffodil will attempt to use - * the daffodil.rootSchema instead. So one or both must be present, but this will always take precedence over - * daffodil.rootSchema if both are present. </li> - * <li>{@code schematron.svrl.file=uri_string_to_output_file}</li> - * <li>{@code daffodil.rootSchema=schema_file_uri_string}</li> - * </ul> - * </li> - * </ul> - * <p> - * And finally get the validator using make as below: - * <pre> - * {@code - * Properties props = new Properties(); - * props.setProperty(XercesValidator.name, "/path/to/validating/schema.xsd"); - * org.apache.daffodil.validation.Validator v = vf.make(props); - * } - * </pre> - * <p> - * or one can load the below properties file as follows: - * <pre> - * {@code - * // schematron.properties - * schematron=/path/to/schema.xsd - * schematron.svrl.file=/path/to/validation/output.txt - * } - * </pre> - * - * <pre> - * {@code - * Properties props = new Properties(); - * props.load(new FileInputStream("schematron.properties")); - * org.apache.daffodil.validation.Validator v = vf.make(props); - * } - * </pre> */ package org.apache.daffodil.api.validation; diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/core/util/TestUtils.scala b/daffodil-core/src/main/scala/org/apache/daffodil/core/util/TestUtils.scala index 94d9cb552..6e575b047 100644 --- a/daffodil-core/src/main/scala/org/apache/daffodil/core/util/TestUtils.scala +++ b/daffodil-core/src/main/scala/org/apache/daffodil/core/util/TestUtils.scala @@ -19,12 +19,12 @@ package org.apache.daffodil.core.util import java.io.ByteArrayInputStream import java.io.InputStream +import java.net.URI import java.nio.channels.Channels import java.nio.channels.ReadableByteChannel import java.nio.channels.WritableByteChannel import java.nio.charset.Charset import java.nio.file.Files -import java.util.Properties import scala.collection.mutable.ArrayBuffer import scala.jdk.CollectionConverters._ import scala.util.Try @@ -34,7 +34,6 @@ import org.apache.daffodil.api import org.apache.daffodil.api.ProcessorFactory import org.apache.daffodil.api.debugger.InteractiveDebuggerRunnerFactory import org.apache.daffodil.api.metadata.MetadataHandler -import org.apache.daffodil.api.validation.Validators import org.apache.daffodil.core.compiler.Compiler import org.apache.daffodil.core.dsom._ import org.apache.daffodil.io.InputSourceDataInputStream @@ -236,7 +235,7 @@ object TestUtils { dp.withDebugger(builtInTracer).withDebugging(true) } else dp - val p = p1.withValidator(Validators.get("limited").make(new Properties)) + val p = p1.withValidation("limited") val outputter = new ScalaXMLInfosetOutputter() val input = InputSourceDataInputStream(is) @@ -370,7 +369,7 @@ class Fakes private () { override def withExternalVariables( extVars: java.util.Map[String, String] ): DFDL.DataProcessor = this - override def withValidator(validator: api.validation.Validator): DFDL.DataProcessor = this + override def withValidation(kind: String, config: URI): DFDL.DataProcessor = this override def withDebugger(dbg: api.debugger.Debugger): DFDL.DataProcessor = this override def withDebugging(flag: Boolean): DFDL.DataProcessor = this @@ -445,14 +444,9 @@ class StreamParser private (val schema: Node) { if (pf.isError) throw new StreamParser.CompileFailure(pf.getDiagnostics) val dataproc1 = pf .onPath("/") - val props = new Properties() val schemaTempFile = Files.createTempFile("streamparser", ".test") FileUtils.write(schemaTempFile.toFile, schema.toString(), Charset.defaultCharset()) - props.setProperty(api.validation.Validator.rootSchemaKey, schemaTempFile.toUri.toString) - val dataproc = dataproc1 - .withValidator( - Validators.get("xerces").make(props) - ) + val dataproc = dataproc1.withValidation("xerces", schemaTempFile.toUri) // .withDebuggerRunner(new TraceDebuggerRunner()) // DAFFODIL-2624 - cannot trace in streaming SAPI // .withDebugging(true) if (dataproc.isError) diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala index 60e481da1..8956dc091 100644 --- a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala +++ b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala @@ -20,18 +20,22 @@ package org.apache.daffodil.runtime1.processors import java.io.File import java.io.IOException import java.io.ObjectOutputStream +import java.net.URI import java.nio.CharBuffer import java.nio.LongBuffer import java.nio.channels.Channels import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Path +import java.util.Properties import java.util.zip.GZIPOutputStream import scala.jdk.CollectionConverters._ import org.apache.daffodil.api import org.apache.daffodil.api.debugger.Debugger import org.apache.daffodil.api.metadata.MetadataHandler +import org.apache.daffodil.api.validation.ValidatorInitializationException +import org.apache.daffodil.api.validation.Validators import org.apache.daffodil.lib.equality._ import org.apache.daffodil.lib.iapi.DaffodilTunables import org.apache.daffodil.lib.iapi.WithDiagnostics @@ -102,9 +106,9 @@ object DataProcessor { variableMap: VariableMap // must be explicitly reset by save method ) extends DataProcessor(ssrd, tunables, variableMap) { - override def withValidator(v: api.validation.Validator): api.DataProcessor = { - if ((v eq DaffodilLimitedValidator) || (v eq NoValidator)) { - super.withValidator(v) + override def withValidation(kind: String, config: URI): api.DataProcessor = { + if (kind == DaffodilLimitedValidator.name || kind == NoValidator.name) { + super.withValidation(kind, config) } else { throw new InvalidUsageException( "Only Limited/No validation allowed when using a restored parser." @@ -191,7 +195,21 @@ class DataProcessor( */ override def clone(): DataProcessor = copy() - override def withValidator(v: api.validation.Validator): api.DataProcessor = { + override def withValidation(kind: String, config: URI): api.DataProcessor = { + val properties = new Properties() + if (config != null) { + val configStr = config.toString + if (configStr.endsWith(".conf") || configStr.endsWith(".properties")) { + try { + properties.load(config.toURL.openStream()) + } catch { + case e: Exception => throw new ValidatorInitializationException(e.getMessage) + } + } else { + properties.setProperty(kind, configStr) + } + } + val v = Validators.get(kind).make(properties) copy(validator = v) } diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/validation/DaffodilLimitedValidator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/validation/DaffodilLimitedValidator.scala index 004f1969f..a84947543 100644 --- a/daffodil-core/src/main/scala/org/apache/daffodil/validation/DaffodilLimitedValidator.scala +++ b/daffodil-core/src/main/scala/org/apache/daffodil/validation/DaffodilLimitedValidator.scala @@ -31,6 +31,8 @@ object DaffodilLimitedValidator extends api.validation.Validator { ): Unit = { // do nothing } + + val name = "limited" } class DaffodilLimitedValidator @@ -42,7 +44,7 @@ class DaffodilLimitedValidator */ class DaffodilLimitedValidatorFactory extends api.validation.ValidatorFactory { - override def name(): String = "limited" + override def name(): String = DaffodilLimitedValidator.name override def make(config: Properties = new Properties()): Validator = { DaffodilLimitedValidator diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/validation/NoValidator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/validation/NoValidator.scala index 0eaa7f5a2..295fe536e 100644 --- a/daffodil-core/src/main/scala/org/apache/daffodil/validation/NoValidator.scala +++ b/daffodil-core/src/main/scala/org/apache/daffodil/validation/NoValidator.scala @@ -31,6 +31,8 @@ object NoValidator extends Validator { ): Unit = { // do nothing } + + val name = "off" } /** @@ -40,7 +42,7 @@ object NoValidator extends Validator { */ class NoValidatorFactory extends api.validation.ValidatorFactory { - override def name(): String = "off" + override def name(): String = NoValidator.name override def make(config: Properties = new Properties()): Validator = { NoValidator diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/validation/XercesValidator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/validation/XercesValidator.scala index ed48f93b3..49ee64cd8 100644 --- a/daffodil-core/src/main/scala/org/apache/daffodil/validation/XercesValidator.scala +++ b/daffodil-core/src/main/scala/org/apache/daffodil/validation/XercesValidator.scala @@ -37,10 +37,9 @@ import org.xml.sax.SAXParseException * * SPI service name: xerces * - * Configuration requirements (only one must be present, but the value of xerces takes precedence if both are present): + * Configuration requirements. * <ul> * <li>xerces=schema_file_uri_string</li> - * <li>daffodil.rootSchema=schema_file_uri_string</li> * </ul> */ class XercesValidatorFactory extends api.validation.ValidatorFactory { @@ -52,20 +51,12 @@ class XercesValidatorFactory extends api.validation.ValidatorFactory { object XercesValidatorFactory { def makeValidator(config: Properties): api.validation.Validator = { - if (config == null) { - throw new api.validation.ValidatorInitializationException( - "invalid configuration: missing xerces path" - ) - } - val schemaPath = config.getProperty(XercesValidator.name) - lazy val defaultSchema = config.getProperty(api.validation.Validator.rootSchemaKey) val schemaFile = { if (!Misc.isNullOrBlank(schemaPath)) schemaPath - else if (!Misc.isNullOrBlank(defaultSchema)) defaultSchema else throw new api.validation.ValidatorInitializationException( - "invalid configuration: xerces validator path was empty" + "invalid configuration: xerces property is empty or not defined" ) } val uri = new URI(schemaFile) diff --git a/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java b/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java index 96b1438b6..022e951cc 100644 --- a/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java +++ b/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java @@ -109,12 +109,6 @@ public class TestAPI { } } - private Properties makeConfig(File schemaFile) throws IOException { - Properties props = new Properties(); - props.setProperty(Validator.rootSchemaKey, schemaFile.toURI().toString()); - return props; - } - /** * This is a test-only helper function used to serialize and deserialize a * DataProcessor to ensure all JAPI classes that need to extend @@ -249,7 +243,7 @@ public class TestAPI { DataProcessor parser = compiler.reload(input); try { - parser = parser.withValidator(Validators.get("xerces").make(makeConfig(schemaFile))); + parser = parser.withValidation("xerces", schemaFile.toURI()); fail(); } catch (InvalidUsageException e) { assertEquals("Only Limited/No validation allowed when using a restored parser.", e.getMessage()); @@ -774,7 +768,7 @@ public class TestAPI { ProcessorFactory pf = c.compileFile(schemaFile); DataProcessor dp = pf.onPath("/"); dp = reserializeDataProcessor(dp); - dp = dp.withValidator(Validators.get("limited").make(new Properties())); + dp = dp.withValidation("limited"); java.io.File file = getResource("/test/api/myData.dat"); java.io.FileInputStream fis = new java.io.FileInputStream(file); @@ -801,7 +795,7 @@ public class TestAPI { java.io.File schemaFile = getResource("/test/api/mySchema1.dfdl.xsd"); ProcessorFactory pf = c.compileFile(schemaFile); DataProcessor dp = pf.onPath("/"); - dp = dp.withValidator(Validators.get("xerces").make(makeConfig(schemaFile))); + dp = dp.withValidation("xerces", schemaFile.toURI()); java.io.File file = getResource("/test/api/myData.dat"); java.io.FileInputStream fis = new java.io.FileInputStream(file); @@ -1365,7 +1359,7 @@ public class TestAPI { java.io.File schemaFile = getResource("/test/api/blob.dfdl.xsd"); ProcessorFactory pf = c.compileFile(schemaFile); DataProcessor dp = pf.onPath("/"); - dp = dp.withValidator(Validators.get("xerces").make(makeConfig(schemaFile))); + dp = dp.withValidation("xerces", schemaFile.toURI()); byte[] data = new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}; ByteArrayInputStream bis = new ByteArrayInputStream(data); @@ -1478,7 +1472,7 @@ public class TestAPI { URI uri = new URI("/test/api/mySchema1.dfdl.xsd"); ProcessorFactory pf = c.compileSource(uri); DataProcessor dp1 = pf.onPath("/"); - DataProcessor dp = dp1.withValidator(Validators.get("xerces").make(makeConfig(getResource(uri.getPath())))); + DataProcessor dp = dp1.withValidation("xerces", getResource(uri.getPath()).toURI()); java.io.File file = getResource("/test/api/myDataBroken.dat"); java.io.FileInputStream fis = new java.io.FileInputStream(file); diff --git a/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorApiExample.java b/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorApiExample.java index 8730f85c4..06c68c638 100644 --- a/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorApiExample.java +++ b/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorApiExample.java @@ -54,7 +54,7 @@ public class ValidatorApiExample { org.apache.daffodil.api.Compiler c = Daffodil.compiler(); java.io.File schemaFile = getResource("/test/api/mySchema5.dfdl.xsd"); ProcessorFactory pf = c.compileFile(schemaFile); - DataProcessor dp = pf.onPath("/").withValidator(Validators.get(PassingValidator.name).make(new Properties())); + DataProcessor dp = pf.onPath("/").withValidation(PassingValidator.name); java.io.File file = getResource("/test/api/myData5.dat"); java.io.FileInputStream fis = new java.io.FileInputStream(file); @@ -70,7 +70,7 @@ public class ValidatorApiExample { org.apache.daffodil.api.Compiler c = Daffodil.compiler(); java.io.File schemaFile = getResource("/test/api/mySchema1.dfdl.xsd"); ProcessorFactory pf = c.compileFile(schemaFile); - DataProcessor dp = pf.onPath("/").withValidator(Validators.get(FailingValidator.name).make(new Properties())); + DataProcessor dp = pf.onPath("/").withValidation(FailingValidator.name); java.io.File file = getResource("/test/api/myData.dat"); java.io.FileInputStream fis = new java.io.FileInputStream(file); @@ -91,9 +91,7 @@ public class ValidatorApiExample { System.err.println(d.toString()); } DataProcessor dp1 = pf.onPath("/"); - Properties props = new Properties(); - props.setProperty(Validator.rootSchemaKey, schemaFile.toURI().toString()); - DataProcessor dp = dp1.withValidator(Validators.get("xerces").make(props)); + DataProcessor dp = dp1.withValidation("xerces", schemaFile.toURI()); java.io.InputStream fis = new ByteArrayInputStream("0".getBytes()); try (InputSourceDataInputStream dis = Infoset.getInputSourceDataInputStream(fis)) { diff --git a/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorSpiExample.java b/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorSpiExample.java index d6dfc1cdf..9acc5c8ad 100644 --- a/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorSpiExample.java +++ b/daffodil-core/src/test/java/org/apache/daffodil/jexample/ValidatorSpiExample.java @@ -18,7 +18,6 @@ package org.apache.daffodil.jexample; import org.apache.daffodil.api.infoset.Infoset; -import org.apache.daffodil.api.validation.ValidatorFactory; import org.apache.daffodil.api.validation.Validators; import org.apache.daffodil.jexample.validation.FailingValidator; import org.apache.daffodil.jexample.validation.PassingValidator; @@ -30,8 +29,6 @@ import org.apache.daffodil.api.infoset.JDOMInfosetOutputter; import org.apache.daffodil.api.InputSourceDataInputStream; import org.junit.Test; -import java.util.Properties; - import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -51,8 +48,7 @@ public class ValidatorSpiExample { ProcessorFactory pf = c.compileFile(schemaFile); assertTrue(Validators.isRegistered(PassingValidator.name)); - ValidatorFactory vf = Validators.get(PassingValidator.name); - DataProcessor dp = pf.onPath("/").withValidator(vf.make(new Properties())); + DataProcessor dp = pf.onPath("/").withValidation(PassingValidator.name); java.io.File file = getResource("/test/api/myData5.dat"); java.io.FileInputStream fis = new java.io.FileInputStream(file); @@ -71,8 +67,7 @@ public class ValidatorSpiExample { ProcessorFactory pf = c.compileFile(schemaFile); assertTrue(Validators.isRegistered(FailingValidator.name)); - ValidatorFactory vf = Validators.get(FailingValidator.name); - DataProcessor dp = pf.onPath("/").withValidator(vf.make(new Properties())); + DataProcessor dp = pf.onPath("/").withValidation(FailingValidator.name); java.io.File file = getResource("/test/api/myData.dat"); java.io.FileInputStream fis = new java.io.FileInputStream(file); diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestValidatorsSPI.scala b/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestValidatorsSPI.scala index 0ceb5f41c..b3a72a8cf 100644 --- a/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestValidatorsSPI.scala +++ b/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestValidatorsSPI.scala @@ -17,7 +17,8 @@ package org.apache.daffodil.lib.validation -import org.apache.daffodil.api.validation.ValidatorFactory +import java.util.Properties + import org.apache.daffodil.api.validation.ValidatorNotRegisteredException import org.apache.daffodil.api.validation.Validators import org.apache.daffodil.validation.XercesValidator @@ -55,7 +56,7 @@ class TestValidatorsSPI { @Test def testPassingValidator(): Unit = { val f = Validators.get(PassingValidator.name) - val v = f.make(ValidatorFactory.makeConfig(schema)) + val v = f.make(new Properties()) val validationHandler = new TestingValidationHandler v.validateXML(infoset, validationHandler) @@ -65,7 +66,7 @@ class TestValidatorsSPI { @Test def testFailingValidator(): Unit = { val f = Validators.get(FailingValidator.name) - val v = f.make(ValidatorFactory.makeConfig(schema)) + val v = f.make(new Properties()) val validationHandler = new TestingValidationHandler v.validateXML(infoset, validationHandler) diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestXercesValidator.scala b/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestXercesValidator.scala index c469f6b7f..f98738086 100644 --- a/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestXercesValidator.scala +++ b/daffodil-core/src/test/scala/org/apache/daffodil/lib/validation/TestXercesValidator.scala @@ -17,7 +17,8 @@ package org.apache.daffodil.lib.validation -import org.apache.daffodil.api.validation.ValidatorFactory +import java.util.Properties + import org.apache.daffodil.api.validation.Validators import org.apache.daffodil.validation.XercesValidator @@ -29,8 +30,10 @@ class TestXercesValidator { val infoset = getClass.getResourceAsStream("/test/validation/testData1Infoset.xml") @Test def testFromSPI(): Unit = { - val f = Validators.get(XercesValidator.name) - val v = f.make(ValidatorFactory.makeConfig(schema)) + val properties = new Properties() + properties.setProperty(XercesValidator.name, schema) + + val v = Validators.get(XercesValidator.name).make(properties) val validationHandler = new TestingValidationHandler v.validateXML(infoset, validationHandler) diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala index c213e3ffd..219ae84a3 100644 --- a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala +++ b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala @@ -29,7 +29,6 @@ import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths import java.util.Optional -import java.util.Properties import javax.xml.XMLConstants import scala.collection.immutable.ArraySeq import scala.jdk.CollectionConverters._ @@ -45,11 +44,8 @@ import org.apache.daffodil.api.exceptions.ExternalVariableException import org.apache.daffodil.api.exceptions.InvalidUsageException import org.apache.daffodil.api.infoset.Infoset import org.apache.daffodil.api.infoset.XMLTextEscapeStyle -import org.apache.daffodil.api.validation.Validator -import org.apache.daffodil.api.validation.Validators import org.apache.daffodil.lib.exceptions.UsageException import org.apache.daffodil.sapi.SAXErrorHandlerForSAPITest -import org.apache.daffodil.validation.NoValidator import org.apache.commons.io.FileUtils import org.junit.Assert.assertArrayEquals @@ -155,7 +151,7 @@ class TestAPI { val dp = reserializeDataProcessor(dp1) .withDebuggerRunner(debugger) .withDebugging(true) - .withValidator(NoValidator) + .withValidation("off") val file = getResource("/test/api/myData.dat") val fis = new java.io.FileInputStream(file) @@ -205,7 +201,7 @@ class TestAPI { .reload(savedParser) .withDebuggerRunner(debugger) .withDebugging(true) - .withValidator(NoValidator) + .withValidation("off") val file = getResource("/test/api/myData.dat") // This test uses a byte array here, just so as to be sure to exercise // the constructor for creating an InputSourceDataInputStream from a byte array @@ -616,7 +612,7 @@ class TestAPI { val dp = reserializeDataProcessor(dp1) .withDebuggerRunner(debugger) .withDebugging(true) - .withValidator(NoValidator) + .withValidation("off") val file = getResource("/test/api/myData.dat") val fis = new java.io.FileInputStream(file) @@ -649,7 +645,7 @@ class TestAPI { .withExternalVariables(extVarsFile) .withDebuggerRunner(debugger) .withDebugging(true) - .withValidator(NoValidator) + .withValidation("off") val file = getResource("/test/api/myData.dat") val fis = new java.io.FileInputStream(file) @@ -681,7 +677,7 @@ class TestAPI { .withExternalVariables(extVarFile) .withDebuggerRunner(debugger) .withDebugging(true) - .withValidator(NoValidator) + .withValidation("off") val file = getResource("/test/api/myData.dat") val fis = new java.io.FileInputStream(file) @@ -730,13 +726,7 @@ class TestAPI { val parser = compiler.reload(input) try { - parser.withValidator( - Validators - .get("xerces") - .make({ - makeConfig(schemaFile) - }) - ) + parser.withValidation("xerces", schemaFile.toURI) fail() } catch { case e: InvalidUsageException => @@ -747,12 +737,6 @@ class TestAPI { } } - private def makeConfig(schemaFile: File) = { - val props = new Properties() - props.setProperty(Validator.rootSchemaKey, schemaFile.toURI.toString) - props - } - @Test def testAPI15(): Unit = { val debugger = new DebuggerRunnerForAPITest() @@ -790,8 +774,7 @@ class TestAPI { val schemaFile = getResource("/test/api/mySchema1.dfdl.xsd") val pf = c.compileFile(schemaFile) val dp1 = pf.onPath("/") - val dp = reserializeDataProcessor(dp1) - .withValidator(Validators.get("limited").make(new Properties())) + val dp = reserializeDataProcessor(dp1).withValidation("limited") val file = getResource("/test/api/myData.dat") val fis = new java.io.FileInputStream(file) Using.resource(Infoset.getInputSourceDataInputStream(fis)) { input => @@ -816,9 +799,7 @@ class TestAPI { val schemaFile = getResource("/test/api/mySchema1.dfdl.xsd") val pf = c.compileFile(schemaFile) val dp1 = pf.onPath("/") - val dp = dp1.withValidator( - Validators.get("xerces").make(makeConfig(schemaFile)) - ) + val dp = dp1.withValidation("xerces", schemaFile.toURI) val file = getResource("/test/api/myData.dat") val fis = new java.io.FileInputStream(file) Using.resource(Infoset.getInputSourceDataInputStream(fis)) { input => @@ -1328,10 +1309,7 @@ class TestAPI { val schemaFile = getResource("/test/api/blob.dfdl.xsd") val pf = c.compileFile(schemaFile) val dp1 = pf.onPath("/") - val dp = dp1 - .withValidator( - Validators.get("xerces").make(makeConfig(schemaFile)) - ) + val dp = dp1.withValidation("xerces", schemaFile.toURI) val data = Array[Byte](0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04) val bis = new ByteArrayInputStream(data) @@ -1438,8 +1416,7 @@ class TestAPI { val uri = new URI("/test/api/mySchema1.dfdl.xsd") val pf = c.compileSource(uri) val dp1 = pf.onPath("/") - val dp = - dp1.withValidator(Validators.get("xerces").make(makeConfig(getResource(uri.getPath)))) + val dp = dp1.withValidation("xerces", getResource(uri.getPath).toURI) val file = getResource("/test/api/myDataBroken.dat") val fis = new java.io.FileInputStream(file) diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorApiExample.scala b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorApiExample.scala deleted file mode 100644 index 3f2dd2b75..000000000 --- a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorApiExample.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.daffodil.sexample - -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test - -class ValidatorApiExample extends ValidatorExamplesSupport { - @Test - def testAlwaysPass(): Unit = - withSchema("/test/api/mySchema5.dfdl.xsd") { dp => - withInput("/test/api/myData5.dat") { input => - val res = dp.withValidator(Always.passes).parse(input, `/dev/null`) - assertFalse(res.isValidationError()) - } - } - - @Test - def testAlwaysFail(): Unit = - withSchema("/test/api/mySchema5.dfdl.xsd") { dp => - withInput("/test/api/myData5.dat") { input => - val res = dp.withValidator(Always.fails).parse(input, `/dev/null`) - assertTrue(res.isValidationError()) - } - } -} diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorExamplesSupport.scala b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorExamplesSupport.scala index 39ce287a2..041d0f460 100644 --- a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorExamplesSupport.scala +++ b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorExamplesSupport.scala @@ -62,21 +62,6 @@ class CustomValidatorFactory extends ValidatorFactory { def make(config: Properties): Validator = new CustomValidator } -class AlwaysValidator(s: Seq[String]) extends Validator { - def validateXML( - document: InputStream, - validationHandler: ValidationHandler - ): Unit = - s.foreach(validationHandler.validationError(_)) -} - -object Always { - def fails: Validator = (_: InputStream, vh: ValidationHandler) => vh.validationError("boom") - def passes: Validator = (_: InputStream, vh: ValidationHandler) => { - // do nothing - } -} - class PassingValidatorFactory extends ValidatorFactory { def name(): String = PassingValidator.name def make(config: Properties): Validator = new TestingValidatorSPI(Seq.empty) diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorSpiExample.scala b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorSpiExample.scala index 93f77ef75..10865543a 100644 --- a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorSpiExample.scala +++ b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/ValidatorSpiExample.scala @@ -17,10 +17,6 @@ package org.apache.daffodil.sexample -import java.util.Properties - -import org.apache.daffodil.api.validation.Validators - import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -32,8 +28,7 @@ class ValidatorSpiExample extends ValidatorExamplesSupport { def testAlwaysPass(): Unit = withSchema("/test/api/mySchema5.dfdl.xsd") { dp => withInput("/test/api/myData5.dat") { input => - val v = Validators.get(PassingValidator.name).make(new Properties()) - val res = dp.withValidator(v).parse(input, `/dev/null`) + val res = dp.withValidation(PassingValidator.name).parse(input, `/dev/null`) assertFalse(res.isValidationError()) } } @@ -42,8 +37,7 @@ class ValidatorSpiExample extends ValidatorExamplesSupport { def testAlwaysFail(): Unit = withSchema("/test/api/mySchema5.dfdl.xsd") { dp => withInput("/test/api/myData5.dat") { input => - val v = Validators.get(FailingValidator.name).make(new Properties()) - val res = dp.withValidator(v).parse(input, `/dev/null`) + val res = dp.withValidation(FailingValidator.name).parse(input, `/dev/null`) assertTrue(res.isValidationError()) } } diff --git a/daffodil-schematron/src/main/scala/org/apache/daffodil/validation/schematron/SchematronValidatorFactory.scala b/daffodil-schematron/src/main/scala/org/apache/daffodil/validation/schematron/SchematronValidatorFactory.scala index 84f916ece..c1b145542 100644 --- a/daffodil-schematron/src/main/scala/org/apache/daffodil/validation/schematron/SchematronValidatorFactory.scala +++ b/daffodil-schematron/src/main/scala/org/apache/daffodil/validation/schematron/SchematronValidatorFactory.scala @@ -29,35 +29,25 @@ import net.sf.saxon.TransformerFactoryImpl /** * Daffodil ValidatorFactory implementation for ISO schematron. - * svrl.file is optional, and if provided a schematron report will be written there. - * If schematron is defined it will use that and expect it to be a .sch file. - * If that's not defined, it will use daffodil.rootSchema and attempt to extract embedded schematron rules + * schematron.svrl.file is optional, and if provided a schematron report will be written there. + * schematron property must be defined, which can either be a .sch file or a DFDL file with + * embedded schematron rules. * * Configuration * * <ul> * <li>schematron=uri_string_to_schematron_file</li> * <li>schematron.svrl.file=uri_string_to_output_file</li> - * <li>daffodil.rootSchema=schema_file_uri_string</li> * </ul> */ object SchematronValidatorFactory { def makeValidator(config: Properties): SchematronValidator = { - - if (config == null) { - throw new api.validation.ValidatorInitializationException( - "invalid configuration: missing schematron path" - ) - } - val schPathValue = config.getProperty(SchematronValidator.name) - lazy val defaultSchema = config.getProperty(api.validation.Validator.rootSchemaKey) val schUri = new URI({ if (!Misc.isNullOrBlank(schPathValue)) schPathValue - else if (!Misc.isNullOrBlank(defaultSchema)) defaultSchema else throw new api.validation.ValidatorInitializationException( - "invalid configuration: schematron path was not an object or string" + "invalid configuration: schematron property is empty or not defined" ) }) val schStream = @@ -94,8 +84,15 @@ object SchematronValidatorFactory { ): SchematronValidator = { val factory = new TransformerFactoryImpl() factory.setURIResolver(DFDLCatalogResolver.get) - val rules = Transforms.from(schematron, schematronID, srcfmt, factory) - new SchematronValidator(Schematron.fromRules(rules), svrlPath) + try { + val rules = Transforms.from(schematron, schematronID, srcfmt, factory) + new SchematronValidator(Schematron.fromRules(rules), svrlPath) + } catch { + case e: Exception => + throw new api.validation.ValidatorInitializationException( + s"failed to create schematron validator: " + e.getMessage + ) + } } } diff --git a/daffodil-schematron/src/test/scala/org/apache/daffodil/validation/schematron/EmbeddedTesting.scala b/daffodil-schematron/src/test/scala/org/apache/daffodil/validation/schematron/EmbeddedTesting.scala index fb7344235..ac9b4416e 100644 --- a/daffodil-schematron/src/test/scala/org/apache/daffodil/validation/schematron/EmbeddedTesting.scala +++ b/daffodil-schematron/src/test/scala/org/apache/daffodil/validation/schematron/EmbeddedTesting.scala @@ -27,7 +27,6 @@ import org.apache.daffodil.api.Diagnostic import org.apache.daffodil.api.ParseResult import org.apache.daffodil.api.infoset.Infoset import org.apache.daffodil.lib.util.Misc -import org.apache.daffodil.validation.schematron.SchSource.Xsd import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -75,12 +74,7 @@ trait EmbeddedTesting { if (pf.isError()) pf.getDiagnostics.forEach(println) assertFalse("Schema did not compile", pf.isError()) - val v = SchematronValidatorFactory.makeValidator( - schema.toURL.openStream(), - schema.toURL.toString(), - Xsd - ) - val dp = pf.onPath("/").withValidator(v) + val dp = pf.onPath("/").withValidation("schematron", schema) f(Validation(dp)) } diff --git a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala index b4241d766..f1bdd391b 100644 --- a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala +++ b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala @@ -28,7 +28,6 @@ import java.nio.CharBuffer import java.nio.LongBuffer import java.nio.charset.CoderResult import java.nio.charset.StandardCharsets -import java.util.Properties import scala.collection.mutable import scala.jdk.CollectionConverters._ import scala.language.postfixOps @@ -41,8 +40,6 @@ import scala.xml.NodeSeq.seqToNodeSeq import scala.xml.SAXParseException import org.apache.daffodil.api -import org.apache.daffodil.api.validation.Validator -import org.apache.daffodil.api.validation.Validators import org.apache.daffodil.io.FormatInfo import org.apache.daffodil.io.InputSourceDataInputStream import org.apache.daffodil.io.processors.charset.BitsCharsetDecoder @@ -412,10 +409,6 @@ class DFDLTestSuite private[tdml] ( val str = (ts \ "@defaultValidation").text if (str == "") defaultValidationDefault else str } - lazy val defaultValidatorName = defaultValidation match { - case "on" => "xerces" - case m => m - } lazy val defaultConfig = { val str = (ts \ "@defaultConfig").text @@ -588,13 +581,10 @@ class DFDLTestSuite private[tdml] ( GlobalTDMLCompileResultCache.cache } - val props = new Properties() - props.setProperty(Validator.rootSchemaKey, suppliedSchema.uriForLoading.toString) - val compileResult = cache.getCompileResult( impl, key, - Validators.get(defaultValidatorName).make(props) + defaultValidation ) compileResult } @@ -634,7 +624,7 @@ abstract class TestCase(testCaseXML: NodeSeq, val parent: DFDLTestSuite) { protected final var processor: TDMLDFDLProcessor = null lazy val defaultRoundTrip: RoundTrip = parent.defaultRoundTrip - lazy val defaultValidatorName: String = parent.defaultValidatorName + lazy val defaultValidation: String = parent.defaultValidation lazy val defaultIgnoreUnexpectedWarnings: Boolean = parent.defaultIgnoreUnexpectedWarnings lazy val defaultIgnoreUnexpectedValidationErrors: Boolean = parent.defaultIgnoreUnexpectedValidationErrors @@ -768,19 +758,10 @@ abstract class TestCase(testCaseXML: NodeSeq, val parent: DFDLTestSuite) { case "false" => false case _ => false } - lazy val validatorName: String = (testCaseXML \ "@validation").text match { - case "" => defaultValidatorName - case "on" => "xerces" + lazy val validation: String = (testCaseXML \ "@validation").text match { + case "" => defaultValidation case mode => mode } - lazy val validator: api.validation.Validator = - Validators - .get(validatorName) - .make({ - val props = new Properties() - props.setProperty(Validator.rootSchemaKey, getSuppliedSchema().uriForLoading.toString) - props - }) protected def runProcessor( compileResult: TDML.CompileResult, @@ -789,7 +770,7 @@ abstract class TestCase(testCaseXML: NodeSeq, val parent: DFDLTestSuite) { errors: Option[Seq[ExpectedErrors]], warnings: Option[Seq[ExpectedWarnings]], validationErrors: Option[Seq[ExpectedValidationErrors]], - validatorName: String, + validation: String, roundTrip: RoundTrip, implString: Option[String] ): Unit @@ -954,8 +935,8 @@ abstract class TestCase(testCaseXML: NodeSeq, val parent: DFDLTestSuite) { } val useSerializedProcessor = - if (validatorName == "xerces") false - else if (defaultValidatorName == "xerces") false + if (validation == "on") false + else if (defaultValidation == "on") false else if (optExpectedWarnings.isDefined) false else true @@ -994,7 +975,7 @@ abstract class TestCase(testCaseXML: NodeSeq, val parent: DFDLTestSuite) { optExpectedErrors, optExpectedWarnings, optExpectedValidationErrors, - validatorName, + validation, roundTrip, implString ) @@ -1074,7 +1055,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) optExpectedErrors: Option[Seq[ExpectedErrors]], optExpectedWarnings: Option[Seq[ExpectedWarnings]], optExpectedValidationErrors: Option[Seq[ExpectedValidationErrors]], - validatorName: String, + validation: String, roundTrip: RoundTrip, implString: Option[String] ) = { @@ -1105,7 +1086,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) nBits, optExpectedWarnings, optExpectedValidationErrors, - validator, + validation, roundTrip, implString, diags.asScala @@ -1132,7 +1113,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) optExpectedErrors, optExpectedWarnings, optExpectedValidationErrors, - validator, + validation, implString, diags.asScala ) @@ -1153,7 +1134,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) optErrors: Option[Seq[ExpectedErrors]], optWarnings: Option[Seq[ExpectedWarnings]], optValidationErrors: Option[Seq[ExpectedValidationErrors]], - validator: api.validation.Validator, + validation: String, implString: Option[String], compileWarnings: Iterable[api.Diagnostic] ): Unit = { @@ -1161,7 +1142,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) try { processor = processor .withExternalDFDLVariables(externalVarBindings) - .withValidator(validator) + .withValidation(validation) } catch { case e: Exception => throw TDMLException(e, implString) } @@ -1380,7 +1361,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) lengthLimitInBits: Long, warnings: Option[Seq[ExpectedWarnings]], validationErrors: Option[Seq[ExpectedValidationErrors]], - validator: api.validation.Validator, + validation: String, roundTripArg: RoundTrip, implString: Option[String], compileWarnings: Iterable[api.Diagnostic] @@ -1389,7 +1370,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) val roundTrip = roundTripArg // change to OnePassRoundTrip to force all parse tests to round trip (to see which fail to round trip) - processor = processor.withValidator(validator) + processor = processor.withValidation(validation) val firstParseTestData = IOUtils.toByteArray(dataToParse) val testInfoset = optExpectedInfoset.get @@ -1576,7 +1557,7 @@ case class UnparserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite) optErrors: Option[Seq[ExpectedErrors]], optWarnings: Option[Seq[ExpectedWarnings]], optValidationErrors: Option[Seq[ExpectedValidationErrors]], - validatorName: String, + validation: String, roundTrip: RoundTrip, implString: Option[String] ) = { @@ -3100,7 +3081,7 @@ case class TDMLCompileResultCache(entryExpireDurationSeconds: Option[Long]) { def getCompileResult( impl: AbstractTDMLDFDLProcessorFactory, key: TDMLCompileResultCacheKey, - defaultValidator: api.validation.Validator + defaultValidation: String ): TDML.CompileResult = this.synchronized { if (entryExpireDurationSeconds.isDefined) { @@ -3145,7 +3126,7 @@ case class TDMLCompileResultCache(entryExpireDurationSeconds: Option[Long]) { // have to rebuild it for every test. This saves memory and should be significantly // faster. val value = compileResult.map { case (diags, proc) => - (diags, proc.withValidator(defaultValidator)) + (diags, proc.withValidation(defaultValidation)) } cache += (key -> TDMLCompileResultCacheValue(value, None)) value diff --git a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala index 26a65dbb0..d64e35779 100644 --- a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala +++ b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala @@ -77,7 +77,7 @@ trait TDMLDFDLProcessor { def withDebugger(db: AnyRef): R - def withValidator(validator: api.validation.Validator): R + def withValidation(validation: String): R def withExternalDFDLVariables(externalVarBindings: Seq[Binding]): R diff --git a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilCTDMLDFDLProcessor.scala b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilCTDMLDFDLProcessor.scala index 41367b307..e707583ba 100644 --- a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilCTDMLDFDLProcessor.scala +++ b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilCTDMLDFDLProcessor.scala @@ -126,7 +126,7 @@ final class DaffodilCTDMLDFDLProcessor(executable: os.Path) extends TDMLDFDLProc override def withDebugging(onOff: Boolean): R = this override def withTracing(onOff: Boolean): R = this override def withDebugger(db: AnyRef): R = this - override def withValidator(validator: api.validation.Validator): R = this + override def withValidation(validation: String): R = this override def withExternalDFDLVariables(externalVarBindings: Seq[Binding]): R = this // Parses the input stream to an infoset and returns a TDMLParseResult diff --git a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilTDMLDFDLProcessor.scala b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilTDMLDFDLProcessor.scala index 84bf07728..4b46739e2 100644 --- a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilTDMLDFDLProcessor.scala +++ b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/processor/tdml/DaffodilTDMLDFDLProcessor.scala @@ -20,6 +20,7 @@ package org.apache.daffodil.processor.tdml import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.OutputStreamWriter +import java.net.URI import java.nio.channels.Channels import java.nio.charset.StandardCharsets import java.nio.file.Files @@ -104,7 +105,8 @@ final class TDMLDFDLProcessorFactory private ( */ private def generateProcessor( pf: DFDL.ProcessorFactory, - useSerializedProcessor: Boolean + useSerializedProcessor: Boolean, + schemaSource: DaffodilSchemaSource ): TDML.CompileResult = { val p = pf.onPath("/") if (p.isError) { @@ -121,7 +123,11 @@ final class TDMLDFDLProcessorFactory private ( } else p } val diags = p.getDiagnostics - Right((diags, new DaffodilTDMLDFDLProcessor(dp.asInstanceOf[DFDL.DataProcessor]))) + val processor = new DaffodilTDMLDFDLProcessor( + dp.asInstanceOf[DFDL.DataProcessor], + schemaSource.uriForLoading + ) + Right(diags, processor) } } @@ -136,7 +142,7 @@ final class TDMLDFDLProcessorFactory private ( val diags = pf.getDiagnostics Left(diags) } else { - val res = this.generateProcessor(pf, useSerializedProcessor) + val res = this.generateProcessor(pf, useSerializedProcessor, schemaSource) res } } @@ -156,20 +162,25 @@ final class TDMLDFDLProcessorFactory private ( val dp = compiler.reload(schemaSource) val diags = dp.getDiagnostics Assert.invariant(diags.asScala.forall { !_.isError }) - Right((diags, new DaffodilTDMLDFDLProcessor(dp.asInstanceOf[DFDL.DataProcessor]))) + val processor = new DaffodilTDMLDFDLProcessor( + dp.asInstanceOf[DFDL.DataProcessor], + schemaSource.uriForLoading + ) + Right(diags, processor) } } } -class DaffodilTDMLDFDLProcessor private (private var dp: api.DataProcessor) +class DaffodilTDMLDFDLProcessor private (private var dp: api.DataProcessor, schemaURI: URI) extends TDMLDFDLProcessor { override type R = DaffodilTDMLDFDLProcessor - def this(ddp: DFDL.DataProcessor) = this(ddp.asInstanceOf[api.DataProcessor]) + def this(ddp: DFDL.DataProcessor, schemaURI: URI) = + this(ddp.asInstanceOf[api.DataProcessor], schemaURI) - private def copy(dp: api.DataProcessor = dp) = new DaffodilTDMLDFDLProcessor(dp) + private def copy(dp: api.DataProcessor = dp) = new DaffodilTDMLDFDLProcessor(dp, schemaURI) private lazy val builtInTracer = new InteractiveDebugger( @@ -204,10 +215,13 @@ class DaffodilTDMLDFDLProcessor private (private var dp: api.DataProcessor) copy(dp = dp.withDebugger(d)) } - override def withValidator( - validator: api.validation.Validator - ): DaffodilTDMLDFDLProcessor = - copy(dp = dp.withValidator(validator)) + override def withValidation(validation: String): DaffodilTDMLDFDLProcessor = { + val validatorName = validation match { + case "on" => "xerces" + case _ => validation + } + copy(dp = dp.withValidation(validatorName, schemaURI)) + } override def withExternalDFDLVariables( externalVarBindings: Seq[Binding] diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/infoset/TestStringAsXml.scala b/daffodil-test/src/test/scala/org/apache/daffodil/infoset/TestStringAsXml.scala index 53c0eac7c..0d27c1151 100644 --- a/daffodil-test/src/test/scala/org/apache/daffodil/infoset/TestStringAsXml.scala +++ b/daffodil-test/src/test/scala/org/apache/daffodil/infoset/TestStringAsXml.scala @@ -21,11 +21,8 @@ import java.io.ByteArrayOutputStream import java.io.InputStream import java.net.URI import java.nio.charset.StandardCharsets -import java.util.Properties import scala.jdk.CollectionConverters._ -import org.apache.daffodil.api.validation.Validator -import org.apache.daffodil.api.validation.Validators import org.apache.daffodil.core.compiler.Compiler import org.apache.daffodil.io.InputSourceDataInputStream import org.apache.daffodil.lib.iapi.URISchemaSource @@ -45,13 +42,8 @@ class TestStringAsXml { URISchemaSource(Misc.uriToDiagnosticFile(dfdlSchemaURI), dfdlSchemaURI) ) val dp = pf.onPath("/") - val props = new Properties() - if (validatingSchema != null) { - props.setProperty(Validator.rootSchemaKey, validatingSchema.toString) - } else { - props.setProperty(Validator.rootSchemaKey, dfdlSchemaURI.toString) - } - dp.withValidator(Validators.get("xerces").make(props)) + val schema = if (validatingSchema != null) validatingSchema else dfdlSchemaURI + dp.withValidation("xerces", schema) }