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)
 
   }
 

Reply via email to