This is an automated email from the ASF dual-hosted git repository.
slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git
The following commit(s) were added to refs/heads/master by this push:
new 81792fa Custom Infoset Validator API with SPI support
81792fa is described below
commit 81792fa3a45b1b5f3cd92a4b3a77ee7e569646e1
Author: John Wass <[email protected]>
AuthorDate: Thu Nov 5 09:09:48 2020 -0500
Custom Infoset Validator API with SPI support
Abstracted Validator implementation to support alternative validation
backends. The original Xerces validation was refactored to become the
XercesValidator and implements this new API.
Support for loading implementations via SPI was added and is used to
support the Daffodil CLI. The CLI is refactored to extend the --validate flag
arguments to accept names used for SPI lookup. The CLI arguments refactoring
keeps backwards comapibility with previous argument handling.
DAFFODIL-2409
---
.../src/main/scala/org/apache/daffodil/Main.scala | 24 +++-
.../apache/daffodil/TestValidatorPatterns.scala | 50 ++++++++
.../scala/org/apache/daffodil/japi/Daffodil.scala | 8 ++
.../daffodil/japi/packageprivate/Utils.scala | 9 --
.../daffodil/example/ValidatorApiExample.java | 74 +++++++++++
.../daffodil/example/ValidatorSpiExample.java | 82 ++++++++++++
.../example/validation/FailingValidator.java | 22 ++--
.../validation/FailingValidatorFactory.java | 23 ++--
.../example/validation/PassingValidator.java | 22 ++--
.../validation/PassingValidatorFactory.java | 23 ++--
.../org.apache.daffodil.api.ValidatorFactory | 17 +++
.../org.apache.daffodil.api.ValidatorFactory | 16 +++
.../org/apache/daffodil/api/ValidationMode.scala | 2 +
.../scala/org/apache/daffodil/api/Validator.scala | 99 +++++++++++++++
.../scala/org/apache/daffodil/util/Validator.scala | 102 ---------------
.../apache/daffodil/validation/Validators.scala | 69 ++++++++++
.../daffodil/validation/XercesValidator.scala | 141 +++++++++++++++++++++
daffodil-lib/src/test/resources/.keep | 0
.../org.apache.daffodil.api.ValidatorFactory | 17 +++
.../resources/test/validation/testData1Infoset.xml | 23 ++++
.../resources/test/validation/testSchema1.dfdl.xsd | 46 +++++++
.../daffodil/validation/TestValidatorsSPI.scala | 70 ++++++++++
.../daffodil/validation/TestXercesValidator.scala} | 22 ++--
.../daffodil/validation/ValidatorSPISupport.scala | 49 +++++++
.../apache/daffodil/processors/DataProcessor.scala | 82 ++++++------
.../apache/daffodil/processors/RuntimeData.scala | 2 +-
.../scala/org/apache/daffodil/sapi/Daffodil.scala | 7 +
.../daffodil/sapi/packageprivate/Utils.scala | 2 +
.../org.apache.daffodil.api.ValidatorFactory | 17 +++
.../daffodil/example/ValidatorApiExample.scala | 29 +++--
.../example/ValidatorExamplesSupport.scala | 95 ++++++++++++++
.../daffodil/example/ValidatorSpiExample.scala | 48 +++++++
project/Dependencies.scala | 1 +
33 files changed, 1085 insertions(+), 208 deletions(-)
diff --git a/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala
b/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala
index fb582d6..5b6f18e 100644
--- a/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala
+++ b/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala
@@ -29,6 +29,8 @@ import java.nio.file.Paths
import java.util.Scanner
import java.util.concurrent.Executors
+import com.typesafe.config.ConfigFactory
+
import scala.concurrent.Await
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
@@ -98,6 +100,7 @@ import org.apache.daffodil.util.Logging
import org.apache.daffodil.util.LoggingDefaults
import org.apache.daffodil.util.Misc
import org.apache.daffodil.util.Timer
+import org.apache.daffodil.validation.Validators
import org.apache.daffodil.xml.QName
import org.apache.daffodil.xml.RefQName
import org.apache.daffodil.xml.DaffodilXMLLoader
@@ -107,6 +110,8 @@ import org.rogach.scallop.ArgType
import org.rogach.scallop.ScallopOption
import org.rogach.scallop.ValueConverter
+import scala.util.matching.Regex
+
class CommandLineSAXErrorHandler() extends org.xml.sax.ErrorHandler with
Logging {
def warning(exception: SAXParseException) = {
@@ -218,11 +223,17 @@ class CLIConf(arguments: Array[String]) extends
scallop.ScallopConf(arguments)
}
implicit def validateConverter = singleArgConverter[ValidationMode.Type]((s:
String) => {
+ import ValidatorPatterns._
s.toLowerCase match {
case "on" => ValidationMode.Full
case "limited" => ValidationMode.Limited
case "off" => ValidationMode.Off
- case _ => throw new Exception("Unrecognized ValidationMode %s. Must be
'on', 'limited' or 'off'.".format(s))
+ case DefaultArgPattern(name, arg) if Validators.isRegistered(name) =>
+ val config = if(arg.endsWith(".conf")) ConfigFactory.load(arg) else
ConfigFactory.parseString(s"$name=$arg")
+ ValidationMode.Custom(Validators.get(name).make(config))
+ case NoArgsPattern(name) if Validators.isRegistered(name) =>
+ ValidationMode.Custom(Validators.get(name).make(ConfigFactory.empty))
+ case _ => throw new Exception("Unrecognized ValidationMode %s. Must be
'on', 'limited', 'off', or name of spi validator.".format(s))
}
})
@@ -326,7 +337,7 @@ class CLIConf(arguments: Array[String]) extends
scallop.ScallopConf(arguments)
val path = opt[String](argName = "path", descr = "path to the node to
create parser.")
val parser = opt[File](short = 'P', argName = "file", descr = "use a
previously saved parser.")
val output = opt[String](argName = "file", descr = "write output to a
given file. If not given or is -, output is written to stdout.")
- val validate: ScallopOption[ValidationMode.Type] =
opt[ValidationMode.Type](short = 'V', default = Some(ValidationMode.Off),
argName = "mode", descr = "the validation mode. 'on', 'limited' or 'off'.")
+ val validate: ScallopOption[ValidationMode.Type] =
opt[ValidationMode.Type](short = 'V', default = Some(ValidationMode.Off),
argName = "mode", descr = "the validation mode. 'on', 'limited', 'off', or spi
name.")
val vars = props[String]('D', keyName = "variable", valueName = "value",
descr = "variables to be used when parsing. An optional namespace may be
provided.")
val tunables = props[String]('T', keyName = "tunable", valueName =
"value", descr = "daffodil tunable to be used when parsing.")
val config = opt[String](short = 'c', argName = "file", descr = "path to
file containing configuration items.")
@@ -391,7 +402,7 @@ class CLIConf(arguments: Array[String]) extends
scallop.ScallopConf(arguments)
val threads = opt[Int](short = 't', argName = "threads", default =
Some(1), descr = "The number of threads to use.")
val path = opt[String](argName = "path", descr = "path to the node to
create parser.")
val parser = opt[File](short = 'P', argName = "file", descr = "use a
previously saved parser.")
- val validate: ScallopOption[ValidationMode.Type] =
opt[ValidationMode.Type](short = 'V', default = Some(ValidationMode.Off),
argName = "mode", descr = "the validation mode. 'on', 'limited' or 'off'.")
+ val validate: ScallopOption[ValidationMode.Type] =
opt[ValidationMode.Type](short = 'V', default = Some(ValidationMode.Off),
argName = "mode", descr = "the validation mode. 'on', 'limited', 'off', or spi
name.")
val vars = props[String]('D', keyName = "variable", valueName = "value",
descr = "variables to be used when processing. An optional namespace may be
provided.")
val tunables = props[String]('T', keyName = "tunable", valueName =
"value", descr = "daffodil tunable to be used when processing.")
val config = opt[String](short = 'c', argName = "file", descr = "path to
file containing configuration items.")
@@ -441,7 +452,7 @@ class CLIConf(arguments: Array[String]) extends
scallop.ScallopConf(arguments)
val path = opt[String](argName = "path", descr = "path to the node to
create parser.")
val parser = opt[File](short = 'P', argName = "file", descr = "use a
previously saved parser.")
val output = opt[String](argName = "file", descr = "write output to file.
If not given or is -, output is written to standard output.")
- val validate: ScallopOption[ValidationMode.Type] =
opt[ValidationMode.Type](short = 'V', default = Some(ValidationMode.Off),
argName = "mode", descr = "the validation mode. 'on', 'limited' or 'off'.")
+ val validate: ScallopOption[ValidationMode.Type] =
opt[ValidationMode.Type](short = 'V', default = Some(ValidationMode.Off),
argName = "mode", descr = "the validation mode. 'on', 'limited', 'off', or spi
name.")
val vars = props[String]('D', keyName = "variable", valueName = "value",
descr = "variables to be used when unparsing. An optional namespace may be
provided.")
val tunables = props[String]('T', keyName = "tunable", valueName =
"value", descr = "daffodil tunable to be used when parsing.")
val config = opt[String](short = 'c', argName = "file", descr = "path to
file containing configuration items.")
@@ -549,6 +560,11 @@ class CLIConf(arguments: Array[String]) extends
scallop.ScallopConf(arguments)
verify()
}
+object ValidatorPatterns {
+ val NoArgsPattern: Regex = "(.+?)".r.anchored
+ val DefaultArgPattern: Regex = "(.+?)=(.+)".r.anchored
+}
+
object Main extends Logging {
val traceCommands = Seq(
diff --git
a/daffodil-cli/src/test/scala/org/apache/daffodil/TestValidatorPatterns.scala
b/daffodil-cli/src/test/scala/org/apache/daffodil/TestValidatorPatterns.scala
new file mode 100644
index 0000000..e4d202f
--- /dev/null
+++
b/daffodil-cli/src/test/scala/org/apache/daffodil/TestValidatorPatterns.scala
@@ -0,0 +1,50 @@
+/*
+ * 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
+
+import org.junit.Test
+import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
+
+class TestValidatorPatterns {
+ @Test def testNoArgsPattern(): Unit = {
+ val vname = "vvv"
+ s"$vname" match {
+ case ValidatorPatterns.DefaultArgPattern(_, _) =>
+ fail("should not have matched")
+ case ValidatorPatterns.NoArgsPattern(v) =>
+ assertEquals(vname, v)
+ case _ =>
+ fail("did not match")
+ }
+ }
+
+ @Test def testDefaultArgsPattern(): Unit = {
+ val vname = "vvv"
+ val varg = "some_default_argument_string"
+ s"$vname=$varg" match {
+ case ValidatorPatterns.DefaultArgPattern(v, arg) =>
+ assertEquals(vname, v)
+ assertEquals(varg, arg)
+ case ValidatorPatterns.NoArgsPattern(_) =>
+ fail("should not have matched")
+ case _ =>
+ fail("did not match")
+ }
+ }
+}
diff --git
a/daffodil-japi/src/main/scala/org/apache/daffodil/japi/Daffodil.scala
b/daffodil-japi/src/main/scala/org/apache/daffodil/japi/Daffodil.scala
index ae20614..a57f4e6 100644
--- a/daffodil-japi/src/main/scala/org/apache/daffodil/japi/Daffodil.scala
+++ b/daffodil-japi/src/main/scala/org/apache/daffodil/japi/Daffodil.scala
@@ -57,6 +57,7 @@ import org.apache.daffodil.processors.{ InvalidUsageException
=> SInvalidUsageEx
import java.net.URI
import org.apache.daffodil.api.URISchemaSource
+import org.apache.daffodil.api.Validator
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe._
import org.apache.daffodil.util.MaybeULong
@@ -603,6 +604,13 @@ class DataProcessor private[japi] (private var dp:
SDataProcessor)
}
/**
+ * Obtain a new [[DataProcessor]] having a specific validator
+ * @param validator validator instance
+ */
+ def withValidator(validator: Validator): DataProcessor =
+ copy(dp =
dp.withValidationMode(org.apache.daffodil.api.ValidationMode.Custom(validator)))
+
+ /**
* Read external variables from a Daffodil configuration file
*
* @see <a target="_blank"
href='https://daffodil.apache.org/configuration/'>Daffodil Configuration
File</a> - Daffodil configuration file format
diff --git
a/daffodil-japi/src/main/scala/org/apache/daffodil/japi/packageprivate/Utils.scala
b/daffodil-japi/src/main/scala/org/apache/daffodil/japi/packageprivate/Utils.scala
index 5090396..4833e4d 100644
---
a/daffodil-japi/src/main/scala/org/apache/daffodil/japi/packageprivate/Utils.scala
+++
b/daffodil-japi/src/main/scala/org/apache/daffodil/japi/packageprivate/Utils.scala
@@ -76,15 +76,6 @@ private[japi] object ValidationConversions {
}
smode
}
-
- def modeFromScala(smode: SValidationMode.Type): ValidationMode = {
- val mode: ValidationMode = smode match {
- case SValidationMode.Off => ValidationMode.Off
- case SValidationMode.Limited => ValidationMode.Limited
- case SValidationMode.Full => ValidationMode.Full
- }
- mode
- }
}
/* A wrapper log writer that scala logging can talk to, which is then forwarded
diff --git
a/daffodil-japi/src/test/java/org/apache/daffodil/example/ValidatorApiExample.java
b/daffodil-japi/src/test/java/org/apache/daffodil/example/ValidatorApiExample.java
new file mode 100644
index 0000000..2564c05
--- /dev/null
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/ValidatorApiExample.java
@@ -0,0 +1,74 @@
+/*
+ * 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.example;
+
+import org.apache.daffodil.example.validation.FailingValidator;
+import org.apache.daffodil.example.validation.PassingValidator;
+import org.apache.daffodil.japi.Daffodil;
+import org.apache.daffodil.japi.DataProcessor;
+import org.apache.daffodil.japi.ParseResult;
+import org.apache.daffodil.japi.ProcessorFactory;
+import org.apache.daffodil.japi.infoset.JDOMInfosetOutputter;
+import org.apache.daffodil.japi.io.InputSourceDataInputStream;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+public class ValidatorApiExample {
+ private java.io.File getResource(String resPath) {
+ try {
+ return new
java.io.File(this.getClass().getResource(resPath).toURI());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Test
+ public void testPassing() throws IOException, ClassNotFoundException {
+ org.apache.daffodil.japi.Compiler c = Daffodil.compiler();
+ java.io.File schemaFile = getResource("/test/japi/mySchema5.dfdl.xsd");
+ ProcessorFactory pf = c.compileFile(schemaFile);
+ DataProcessor dp = pf.onPath("/").withValidator(new
PassingValidator());
+
+ java.io.File file = getResource("/test/japi/myData5.dat");
+ java.io.FileInputStream fis = new java.io.FileInputStream(file);
+ InputSourceDataInputStream dis = new InputSourceDataInputStream(fis);
+ JDOMInfosetOutputter outputter = new JDOMInfosetOutputter();
+ ParseResult res = dp.parse(dis, outputter);
+
+ assertFalse(res.isValidationError());
+ }
+
+ @Test
+ public void testFailing() throws IOException, ClassNotFoundException {
+ org.apache.daffodil.japi.Compiler c = Daffodil.compiler();
+ java.io.File schemaFile = getResource("/test/japi/mySchema1.dfdl.xsd");
+ ProcessorFactory pf = c.compileFile(schemaFile);
+ DataProcessor dp = pf.onPath("/").withValidator(new
FailingValidator());
+
+ java.io.File file = getResource("/test/japi/myData.dat");
+ java.io.FileInputStream fis = new java.io.FileInputStream(file);
+ InputSourceDataInputStream dis = new InputSourceDataInputStream(fis);
+ JDOMInfosetOutputter outputter = new JDOMInfosetOutputter();
+ ParseResult res = dp.parse(dis, outputter);
+
+ assertTrue(res.isValidationError());
+ }
+}
diff --git
a/daffodil-japi/src/test/java/org/apache/daffodil/example/ValidatorSpiExample.java
b/daffodil-japi/src/test/java/org/apache/daffodil/example/ValidatorSpiExample.java
new file mode 100644
index 0000000..83838a4
--- /dev/null
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/ValidatorSpiExample.java
@@ -0,0 +1,82 @@
+/*
+ * 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.example;
+
+import com.typesafe.config.ConfigFactory;
+import org.apache.daffodil.api.ValidatorFactory;
+import org.apache.daffodil.example.validation.FailingValidator;
+import org.apache.daffodil.example.validation.PassingValidator;
+import org.apache.daffodil.japi.Daffodil;
+import org.apache.daffodil.japi.DataProcessor;
+import org.apache.daffodil.japi.ParseResult;
+import org.apache.daffodil.japi.ProcessorFactory;
+import org.apache.daffodil.japi.infoset.JDOMInfosetOutputter;
+import org.apache.daffodil.japi.io.InputSourceDataInputStream;
+import org.apache.daffodil.validation.Validators;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ValidatorSpiExample {
+ private java.io.File getResource(String resPath) {
+ try {
+ return new
java.io.File(this.getClass().getResource(resPath).toURI());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Test
+ public void testPassing() throws Exception {
+ org.apache.daffodil.japi.Compiler c = Daffodil.compiler();
+ java.io.File schemaFile = getResource("/test/japi/mySchema5.dfdl.xsd");
+ ProcessorFactory pf = c.compileFile(schemaFile);
+
+ assertTrue(Validators.isRegistered(PassingValidator.name));
+ ValidatorFactory vf = Validators.get(PassingValidator.name);
+ DataProcessor dp =
pf.onPath("/").withValidator(vf.make(ConfigFactory.empty()));
+
+ java.io.File file = getResource("/test/japi/myData5.dat");
+ java.io.FileInputStream fis = new java.io.FileInputStream(file);
+ InputSourceDataInputStream dis = new InputSourceDataInputStream(fis);
+ JDOMInfosetOutputter outputter = new JDOMInfosetOutputter();
+ ParseResult res = dp.parse(dis, outputter);
+
+ assertFalse(res.isValidationError());
+ }
+
+ @Test
+ public void testFailing() throws Exception {
+ org.apache.daffodil.japi.Compiler c = Daffodil.compiler();
+ java.io.File schemaFile = getResource("/test/japi/mySchema1.dfdl.xsd");
+ ProcessorFactory pf = c.compileFile(schemaFile);
+
+ assertTrue(Validators.isRegistered(FailingValidator.name));
+ ValidatorFactory vf = Validators.get(FailingValidator.name);
+ DataProcessor dp =
pf.onPath("/").withValidator(vf.make(ConfigFactory.empty()));
+
+ java.io.File file = getResource("/test/japi/myData.dat");
+ java.io.FileInputStream fis = new java.io.FileInputStream(file);
+ InputSourceDataInputStream dis = new InputSourceDataInputStream(fis);
+ JDOMInfosetOutputter outputter = new JDOMInfosetOutputter();
+ ParseResult res = dp.parse(dis, outputter);
+
+ assertTrue(res.isValidationError());
+ }
+}
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/FailingValidator.java
similarity index 61%
copy from
daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
copy to
daffodil-japi/src/test/java/org/apache/daffodil/example/validation/FailingValidator.java
index 3dc7a65..335c40d 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/FailingValidator.java
@@ -15,15 +15,19 @@
* limitations under the License.
*/
-package org.apache.daffodil.api
+package org.apache.daffodil.example.validation;
-import org.apache.daffodil.util.Enum
+import org.apache.daffodil.api.ValidationResult;
+import org.apache.daffodil.api.Validator;
-object ValidationMode extends Enum {
- sealed abstract class Type protected (val mode: Int) extends EnumValueType
with Ordered[Type] with Serializable {
- def compare(that: ValidationMode.Type) = this.mode - that.mode
- }
- case object Off extends Type(10)
- case object Limited extends Type(20)
- case object Full extends Type(30)
+import java.io.InputStream;
+import java.util.Collections;
+
+public class FailingValidator implements Validator {
+ public static final String name = "failing-japi-validator";
+
+ @Override
+ public ValidationResult validateXML(InputStream document) {
+ return new ValidationResult(Collections.emptyList(),
Collections.singletonList(() -> "boom"));
+ }
}
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/FailingValidatorFactory.java
similarity index 65%
copy from
daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
copy to
daffodil-japi/src/test/java/org/apache/daffodil/example/validation/FailingValidatorFactory.java
index 3dc7a65..41da8b6 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/FailingValidatorFactory.java
@@ -15,15 +15,20 @@
* limitations under the License.
*/
-package org.apache.daffodil.api
+package org.apache.daffodil.example.validation;
-import org.apache.daffodil.util.Enum
+import com.typesafe.config.Config;
+import org.apache.daffodil.api.Validator;
+import org.apache.daffodil.api.ValidatorFactory;
-object ValidationMode extends Enum {
- sealed abstract class Type protected (val mode: Int) extends EnumValueType
with Ordered[Type] with Serializable {
- def compare(that: ValidationMode.Type) = this.mode - that.mode
- }
- case object Off extends Type(10)
- case object Limited extends Type(20)
- case object Full extends Type(30)
+public class FailingValidatorFactory implements ValidatorFactory {
+ @Override
+ public String name() {
+ return FailingValidator.name;
+ }
+
+ @Override
+ public Validator make(Config config) {
+ return new FailingValidator();
+ }
}
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/PassingValidator.java
similarity index 62%
copy from
daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
copy to
daffodil-japi/src/test/java/org/apache/daffodil/example/validation/PassingValidator.java
index 3dc7a65..a6fe260 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/PassingValidator.java
@@ -15,15 +15,19 @@
* limitations under the License.
*/
-package org.apache.daffodil.api
+package org.apache.daffodil.example.validation;
-import org.apache.daffodil.util.Enum
+import org.apache.daffodil.api.ValidationResult;
+import org.apache.daffodil.api.Validator;
-object ValidationMode extends Enum {
- sealed abstract class Type protected (val mode: Int) extends EnumValueType
with Ordered[Type] with Serializable {
- def compare(that: ValidationMode.Type) = this.mode - that.mode
- }
- case object Off extends Type(10)
- case object Limited extends Type(20)
- case object Full extends Type(30)
+import java.io.InputStream;
+import java.util.Collections;
+
+public class PassingValidator implements Validator {
+ public static final String name = "passing-japi-validator";
+
+ @Override
+ public ValidationResult validateXML(InputStream document) {
+ return new ValidationResult(Collections.emptyList(),
Collections.emptyList());
+ }
}
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/PassingValidatorFactory.java
similarity index 65%
copy from
daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
copy to
daffodil-japi/src/test/java/org/apache/daffodil/example/validation/PassingValidatorFactory.java
index 3dc7a65..848345b 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/validation/PassingValidatorFactory.java
@@ -15,15 +15,20 @@
* limitations under the License.
*/
-package org.apache.daffodil.api
+package org.apache.daffodil.example.validation;
-import org.apache.daffodil.util.Enum
+import com.typesafe.config.Config;
+import org.apache.daffodil.api.Validator;
+import org.apache.daffodil.api.ValidatorFactory;
-object ValidationMode extends Enum {
- sealed abstract class Type protected (val mode: Int) extends EnumValueType
with Ordered[Type] with Serializable {
- def compare(that: ValidationMode.Type) = this.mode - that.mode
- }
- case object Off extends Type(10)
- case object Limited extends Type(20)
- case object Full extends Type(30)
+public class PassingValidatorFactory implements ValidatorFactory {
+ @Override
+ public String name() {
+ return PassingValidator.name;
+ }
+
+ @Override
+ public Validator make(Config config) {
+ return new PassingValidator();
+ }
}
diff --git
a/daffodil-japi/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
b/daffodil-japi/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
new file mode 100644
index 0000000..df7a270
--- /dev/null
+++
b/daffodil-japi/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
@@ -0,0 +1,17 @@
+# 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.
+#
+org.apache.daffodil.example.validation.PassingValidatorFactory
+org.apache.daffodil.example.validation.FailingValidatorFactory
diff --git
a/daffodil-lib/src/main/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
b/daffodil-lib/src/main/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
new file mode 100644
index 0000000..ac7b5d7
--- /dev/null
+++
b/daffodil-lib/src/main/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
@@ -0,0 +1,16 @@
+# 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.
+#
+org.apache.daffodil.validation.XercesValidatorFactory
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
index 3dc7a65..d982a42 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
@@ -26,4 +26,6 @@ object ValidationMode extends Enum {
case object Off extends Type(10)
case object Limited extends Type(20)
case object Full extends Type(30)
+
+ case class Custom(v: Validator) extends Type( 100)
}
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/Validator.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/api/Validator.scala
new file mode 100644
index 0000000..1a1c385
--- /dev/null
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/api/Validator.scala
@@ -0,0 +1,99 @@
+/*
+ * 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.api
+
+import com.typesafe.config.Config
+
+/**
+ * Implement this trait to provide custom validation logic
+ *
+ * The Validator implementations are expected to be thread safe
+ *
+ * @see [[org.apache.daffodil.validation.XercesValidator]] for example of
using ThreadLocal for thread safety
+ */
+trait Validator {
+ def validateXML(document: java.io.InputStream): ValidationResult
+}
+
+/**
+ * Implement this trait and register with SPI to provide runtime discovery of
custom Validator implementations
+ *
+ * The factory implementations are expected to be thread safe
+ */
+trait ValidatorFactory {
+ /**
+ * Unique name of this Validator service
+ * @return registered name of the validator factory
+ */
+ def name(): String
+
+ /**
+ * The factory method to generate the Validator instance
+ * @param config com.typesafe.config.Config to pass to validator instance
+ * @return [[org.apache.daffodil.api.Validator]] instance ready to execute
+ * @throws org.apache.daffodil.api.ValidatorInitializationException when
initialization fails
+ */
+ @throws(classOf[ValidatorInitializationException])
+ def make(config: Config): Validator
+}
+
+/**
+ * Results of a validation execution
+ * @param warnings [[org.apache.daffodil.api.ValidationWarning]] objects
+ * @param errors [[org.apache.daffodil.api.ValidationFailure]] objects
+ */
+final case class ValidationResult(warnings:
java.util.Collection[ValidationWarning], errors:
java.util.Collection[ValidationFailure])
+
+object ValidationResult {
+ /**
+ * an empty [[org.apache.daffodil.api.ValidationResult]]
+ */
+ val empty: ValidationResult = ValidationResult(Seq.empty, Seq.empty)
+
+ def apply(warnings: Seq[ValidationWarning], errors: Seq[ValidationFailure]):
ValidationResult = {
+ import scala.collection.JavaConverters.asJavaCollectionConverter
+ ValidationResult(warnings.asJavaCollection, errors.asJavaCollection)
+ }
+}
+
+/**
+ * Represents a non-fatal validation notification
+ */
+trait ValidationWarning {
+ def getMessage: String
+}
+
+/**
+ * Represents a fatal validation notification
+ */
+trait ValidationFailure {
+ def getMessage: String
+}
+
+/**
+ * Represents a fatal validation notification backed by a Throwable
+ */
+trait ValidationException {
+ def getCause: Throwable
+}
+
+/**
+ * Thrown when a validator fails to initialize
+ * @param message used in the underlying Exception
+ */
+final case class ValidatorInitializationException(message: String) extends
Exception(message)
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/util/Validator.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/util/Validator.scala
deleted file mode 100644
index 1e5684a..0000000
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/util/Validator.scala
+++ /dev/null
@@ -1,102 +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.util
-
-import java.net.URI
-
-import scala.collection.mutable
-import scala.xml.parsing.NoBindingFactoryAdapter
-
-import javax.xml.XMLConstants
-import javax.xml.transform.stream.StreamSource
-import org.apache.daffodil.xml.DFDLCatalogResolver
-import org.xml.sax.ErrorHandler
-
-/**
- * Use this for extra validation passes in the TDML Runner
- * to do a validation pass on the TDML expected Infoset w.r.t. the model and to
- * do a validation pass on the actual result w.r.t. the model as an XML
document.
- */
-object Validator extends NoBindingFactoryAdapter {
-
- private type CacheType = mutable.HashMap[Seq[String],
javax.xml.validation.Validator]
-
- private val validationSchemaCache =
- new ThreadLocal[CacheType] {
- override def initialValue =
- new CacheType
- }
-
- def validateXMLSources(schemaFileNames: Seq[String], document:
java.io.InputStream, errHandler: ErrorHandler): Unit = {
- val cache = validationSchemaCache.get()
- val validator = {
- val optCachedValidator = cache.get(schemaFileNames)
- optCachedValidator match {
- case Some(cachedValidator) => {
- cachedValidator.reset()
- // reset takes it back to the original state at the point we call
- // newValidator. So we need to re-set the features, resolvers,
- // and handlers
- val resolver = DFDLCatalogResolver.get
- val validator : javax.xml.validation.Validator =
- initializeValidator(cachedValidator, errHandler, resolver)
- validator
- }
- case None => {
- val schemaSources: Seq[javax.xml.transform.Source] =
schemaFileNames.map { fn =>
- {
- val uri = new URI(fn)
- val is = uri.toURL.openStream()
- val stream = new StreamSource(is)
- stream.setSystemId(uri.toString) // must set this so that
relative URIs will be created for import/include files.
- stream
- }
- }
-
- val factory = new
org.apache.xerces.jaxp.validation.XMLSchemaFactory()
- factory.setErrorHandler(errHandler)
- val resolver = DFDLCatalogResolver.get
- factory.setResourceResolver(resolver)
- val schema = factory.newSchema(schemaSources.toArray)
- val validator = initializeValidator(schema.newValidator(),
errHandler, resolver)
- cache.put(schemaFileNames, validator)
- validator
- }
- }
- }
- val documentSource = new StreamSource(document)
- validator.validate(documentSource)
- }
-
- def initializeValidator(validator: javax.xml.validation.Validator,
errHandler: ErrorHandler, resolver: DFDLCatalogResolver):
- javax.xml.validation.Validator = {
- validator.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
- //
- validator.setFeature("http://xml.org/sax/features/validation", true)
-
- // If you enable the feature below, it seems to do no validation at all.
Just passes.
- //
validator.setFeature("http://apache.org/xml/features/validation/dynamic", true)
-
- validator.setFeature("http://apache.org/xml/features/validation/schema",
true)
-
validator.setFeature("http://apache.org/xml/features/validation/schema-full-checking",
true)
- validator.setErrorHandler(errHandler)
- validator.setResourceResolver(resolver)
- validator
- }
-}
-
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/validation/Validators.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/validation/Validators.scala
new file mode 100644
index 0000000..dc9c56a
--- /dev/null
+++
b/daffodil-lib/src/main/scala/org/apache/daffodil/validation/Validators.scala
@@ -0,0 +1,69 @@
+/*
+ * 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.validation
+
+import java.util.ServiceLoader
+
+import org.apache.daffodil.api.ValidatorFactory
+
+import scala.collection.JavaConverters._
+
+/**
+ * Access SPI registered [[org.apache.daffodil.api.ValidatorFactory]]
instances.
+ *
+ * Registered instances provide a unique name for lookup.
+ */
+object Validators {
+ private lazy val impls: Map[String, ValidatorFactory] =
+ ServiceLoader
+ .load(classOf[ValidatorFactory])
+ .iterator()
+ .asScala
+ .map(v => v.name() -> v)
+ .toMap
+
+ /**
+ * Get the factory by name or throw
+ * @param name registered name of the validator factory
+ * @throws ValidatorNotRegisteredException when factory is not found in the
registered services
+ * @return [[org.apache.daffodil.api.ValidatorFactory]] the factory instance
+ */
+ @throws(classOf[ValidatorNotRegisteredException])
+ def get(name: String): ValidatorFactory = impls.getOrElse(name, throw
ValidatorNotRegisteredException(name))
+
+ /**
+ * Optionally find the factory
+ * @param name registered name of the validator factory
+ * @return [[org.apache.daffodil.api.ValidatorFactory]] optional factory
instance
+ */
+ def find(name: String): Option[ValidatorFactory] = impls.get(name)
+
+ /**
+ * Check for registration of named factory
+ * @param name registered name of the validator factory
+ * @return is factory registered
+ */
+ def isRegistered(name: String): Boolean = impls.contains(name)
+}
+
+/**
+ * Thrown when the by-name lookup of a validator fails
+ * @param name the requested validator factory name
+ */
+case class ValidatorNotRegisteredException(name: String)
+ extends Exception(s"No ValidatorFactory is registered as $name")
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/validation/XercesValidator.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/validation/XercesValidator.scala
new file mode 100644
index 0000000..a88084b
--- /dev/null
+++
b/daffodil-lib/src/main/scala/org/apache/daffodil/validation/XercesValidator.scala
@@ -0,0 +1,141 @@
+/*
+ * 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.validation
+
+import java.net.URI
+
+import com.typesafe.config.Config
+import com.typesafe.config.ConfigFactory
+import com.typesafe.config.ConfigValueFactory
+import javax.xml.XMLConstants
+import javax.xml.transform.stream.StreamSource
+import org.apache.daffodil.api.ValidationException
+import org.apache.daffodil.api.ValidationFailure
+import org.apache.daffodil.api.ValidationResult
+import org.apache.daffodil.api.ValidationWarning
+import org.apache.daffodil.api.Validator
+import org.apache.daffodil.api.ValidatorFactory
+import org.apache.daffodil.validation.XercesValidator.XercesValidatorImpl
+import org.apache.daffodil.xml.DFDLCatalogResolver
+import org.xml.sax.ErrorHandler
+import org.xml.sax.SAXParseException
+
+import scala.collection.JavaConverters._
+import scala.xml.SAXException
+
+/**
+ * Provides a XercesValidator instance
+ *
+ * SPI service name: xerces
+ * Configuration requirements:
+ * - xerces: List[String] - schema file names
+ */
+class XercesValidatorFactory extends ValidatorFactory {
+ def name(): String = XercesValidator.name
+ def make(config: Config): Validator =
XercesValidatorFactory.makeValidator(config)
+}
+
+object XercesValidatorFactory {
+ def makeValidator(config: Config): Validator = {
+ val schemaFiles =
+ if(config.hasPath(XercesValidator.name))
+ config.getStringList(XercesValidator.name).asScala
+ else Seq.empty
+ new XercesValidator(schemaFiles)
+ }
+
+ def makeConfig(uris: Seq[String]): Config = {
+ val v = ConfigValueFactory.fromIterable(uris.asJava)
+ ConfigFactory.parseMap(Map(XercesValidator.name -> v).asJava)
+ }
+}
+
+/**
+ * Use this for extra validation passes in the TDML Runner
+ * to do a validation pass on the TDML expected Infoset w.r.t. the model and to
+ * do a validation pass on the actual result w.r.t. the model as an XML
document.
+ */
+class XercesValidator(schemaFileNames: Seq[String])
+ extends Validator {
+
+ private val schemaSources: Seq[javax.xml.transform.Source] =
schemaFileNames.map { fn =>
+ val uri = new URI(fn)
+ val is = uri.toURL.openStream()
+ val stream = new StreamSource(is)
+ stream.setSystemId(uri.toString) // must set this so that relative URIs
will be created for import/include files.
+ stream
+ }
+
+ private val factory = new
org.apache.xerces.jaxp.validation.XMLSchemaFactory()
+ private val resolver = DFDLCatalogResolver.get
+ factory.setResourceResolver(resolver)
+
+ private val schema = factory.newSchema(schemaSources.toArray)
+
+ private val validator = new ThreadLocal[XercesValidatorImpl] {
+ override def initialValue(): XercesValidatorImpl =
+ initializeValidator(schema.newValidator(), resolver)
+ }
+
+ def validateXML(document: java.io.InputStream): ValidationResult = {
+ val documentSource = new StreamSource(document)
+
+ // get the validator instance for this thread
+ val xv = validator.get()
+
+ // create a new error handler for this execution
+ val eh = new XercesErrorHandler
+ xv.setErrorHandler(eh)
+
+ // validate the document
+ xv.validate(documentSource)
+
+ // error handler contents as daffodil result
+ ValidationResult(eh.warnings, eh.errors)
+ }
+
+ private def initializeValidator(validator: XercesValidatorImpl, resolver:
DFDLCatalogResolver): XercesValidatorImpl = {
+ validator.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
+ validator.setFeature("http://xml.org/sax/features/validation", true)
+ validator.setFeature("http://apache.org/xml/features/validation/schema",
true)
+
validator.setFeature("http://apache.org/xml/features/validation/schema-full-checking",
true)
+ validator.setResourceResolver(resolver)
+ validator
+ }
+}
+
+object XercesValidator {
+ private type XercesValidatorImpl = javax.xml.validation.Validator
+ val name = "xerces"
+}
+
+private class XercesErrorHandler extends ErrorHandler {
+ private var e = List.empty[ValidationFailure]
+ private var w = List.empty[ValidationWarning]
+
+ def errors: Seq[ValidationFailure] = e
+ def warnings: Seq[ValidationWarning] = w
+
+ override def warning(spe: SAXParseException): Unit = w :+=
SaxValidationWarning(spe)
+ override def error(spe: SAXParseException): Unit = e :+=
SaxValidationError(spe)
+ override def fatalError(spe: SAXParseException): Unit = e :+=
SaxValidationError(spe)
+}
+
+sealed abstract class SaxValidationResult(e: SAXException) extends
Exception(e) with ValidationException
+case class SaxValidationError(e: SAXException) extends SaxValidationResult(e)
with ValidationFailure with ValidationException
+case class SaxValidationWarning(e: SAXException) extends
SaxValidationResult(e) with ValidationWarning with ValidationException
diff --git a/daffodil-lib/src/test/resources/.keep
b/daffodil-lib/src/test/resources/.keep
deleted file mode 100644
index e69de29..0000000
diff --git
a/daffodil-lib/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
b/daffodil-lib/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
new file mode 100644
index 0000000..f6aeb35
--- /dev/null
+++
b/daffodil-lib/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
@@ -0,0 +1,17 @@
+# 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.
+#
+org.apache.daffodil.validation.PassingValidatorFactory
+org.apache.daffodil.validation.FailingValidatorFactory
diff --git
a/daffodil-lib/src/test/resources/test/validation/testData1Infoset.xml
b/daffodil-lib/src/test/resources/test/validation/testData1Infoset.xml
new file mode 100644
index 0000000..3868b51
--- /dev/null
+++ b/daffodil-lib/src/test/resources/test/validation/testData1Infoset.xml
@@ -0,0 +1,23 @@
+<!--
+ 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.
+-->
+
+<tns:e1 xmlns:tns="http://example.com">
+ <elementGroup>
+ <e2>12</e2>
+ <e3>345678</e3>
+ </elementGroup>
+</tns:e1>
diff --git
a/daffodil-lib/src/test/resources/test/validation/testSchema1.dfdl.xsd
b/daffodil-lib/src/test/resources/test/validation/testSchema1.dfdl.xsd
new file mode 100644
index 0000000..ca953b2
--- /dev/null
+++ b/daffodil-lib/src/test/resources/test/validation/testSchema1.dfdl.xsd
@@ -0,0 +1,46 @@
+<!--
+ 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.
+-->
+
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://example.com"
xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tns="http://example.com">
+
+ <annotation>
+ <appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:format ref="tns:GeneralFormat" />
+ </appinfo>
+ </annotation>
+
+ <include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+ <element name="e1" dfdl:terminator="%NL;" type="tns:e1Type"/>
+
+ <complexType name="e1Type">
+ <sequence dfdl:initiator="" dfdl:separator="%SP;"
dfdl:separatorPolicy="suppressed">
+ <element name="elementGroup">
+ <complexType>
+ <sequence dfdl:separator=":">
+ <element name="e2" type="xsd:int" dfdl:lengthKind="explicit"
dfdl:length="2" />
+ <element name="e3" type="xsd:string" dfdl:lengthKind="explicit"
dfdl:length="6" />
+ </sequence>
+ </complexType>
+ </element>
+ </sequence>
+ </complexType>
+
+</schema>
diff --git
a/daffodil-lib/src/test/scala/org/apache/daffodil/validation/TestValidatorsSPI.scala
b/daffodil-lib/src/test/scala/org/apache/daffodil/validation/TestValidatorsSPI.scala
new file mode 100644
index 0000000..2e8484e
--- /dev/null
+++
b/daffodil-lib/src/test/scala/org/apache/daffodil/validation/TestValidatorsSPI.scala
@@ -0,0 +1,70 @@
+/*
+ * 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.validation
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class TestValidatorsSPI {
+ val schema =
getClass.getResource("/test/validation/testSchema1.dfdl.xsd").toURI.toString
+ val infoset =
getClass.getResourceAsStream("/test/validation/testData1Infoset.xml")
+
+ @Test def testValidatorGetNotFoundThrows(): Unit = {
+ assertThrows(classOf[ValidatorNotRegisteredException], () =>
Validators.get("dont exist"))
+ }
+
+ @Test def testValidatorFindNotFoundNone(): Unit = {
+ assertTrue(Validators.find("dont exist").isEmpty)
+ }
+
+ @Test def testValidatorNonExists(): Unit = {
+ assertFalse(Validators.isRegistered("dont exist"))
+ }
+
+ @Test def testDefaultIsRegistered(): Unit = {
+ val defaultName = XercesValidator.name
+ val defaultF = Validators.get(defaultName)
+
+ assertEquals(defaultName, defaultF.name())
+ assertTrue(Validators.find(defaultName).nonEmpty)
+ assertTrue(Validators.isRegistered(defaultName))
+ }
+
+ @Test def testPassingValidator(): Unit = {
+ val f = Validators.get(PassingValidator.name)
+ val v = f.make(XercesValidatorFactory.makeConfig(Seq(schema)))
+ val r = v.validateXML(infoset)
+
+ assertTrue(r.warnings.isEmpty)
+ assertTrue(r.errors.isEmpty)
+ }
+
+ @Test def testFailingValidator(): Unit = {
+ val f = Validators.get(FailingValidator.name)
+ val v = f.make(XercesValidatorFactory.makeConfig(Seq(schema)))
+ val r = v.validateXML(infoset)
+
+ assertTrue(r.warnings.isEmpty)
+ assertFalse(r.errors.isEmpty)
+
+ assertEquals(r.errors.iterator().next().getMessage, "boom")
+ }
+}
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
b/daffodil-lib/src/test/scala/org/apache/daffodil/validation/TestXercesValidator.scala
similarity index 59%
copy from
daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
copy to
daffodil-lib/src/test/scala/org/apache/daffodil/validation/TestXercesValidator.scala
index 3dc7a65..47621ff 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
+++
b/daffodil-lib/src/test/scala/org/apache/daffodil/validation/TestXercesValidator.scala
@@ -15,15 +15,21 @@
* limitations under the License.
*/
-package org.apache.daffodil.api
+package org.apache.daffodil.validation
-import org.apache.daffodil.util.Enum
+import org.junit.Test
+import org.junit.Assert.assertTrue
-object ValidationMode extends Enum {
- sealed abstract class Type protected (val mode: Int) extends EnumValueType
with Ordered[Type] with Serializable {
- def compare(that: ValidationMode.Type) = this.mode - that.mode
+class TestXercesValidator {
+ val schema =
getClass.getResource("/test/validation/testSchema1.dfdl.xsd").toURI.toString
+ val infoset =
getClass.getResourceAsStream("/test/validation/testData1Infoset.xml")
+
+ @Test def testFromSPI(): Unit = {
+ val f = Validators.get(XercesValidator.name)
+ val v = f.make(XercesValidatorFactory.makeConfig(Seq(schema)))
+ val r = v.validateXML(infoset)
+
+ assertTrue(r.warnings.isEmpty)
+ assertTrue(r.errors.isEmpty)
}
- case object Off extends Type(10)
- case object Limited extends Type(20)
- case object Full extends Type(30)
}
diff --git
a/daffodil-lib/src/test/scala/org/apache/daffodil/validation/ValidatorSPISupport.scala
b/daffodil-lib/src/test/scala/org/apache/daffodil/validation/ValidatorSPISupport.scala
new file mode 100644
index 0000000..3d81f14
--- /dev/null
+++
b/daffodil-lib/src/test/scala/org/apache/daffodil/validation/ValidatorSPISupport.scala
@@ -0,0 +1,49 @@
+/*
+ * 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.validation
+
+import java.io.InputStream
+
+import com.typesafe.config.Config
+import org.apache.daffodil.api.ValidationFailure
+import org.apache.daffodil.api.ValidationResult
+import org.apache.daffodil.api.ValidationWarning
+import org.apache.daffodil.api.Validator
+import org.apache.daffodil.api.ValidatorFactory
+
+class PassingValidatorFactory extends ValidatorFactory {
+ def name(): String = PassingValidator.name
+ def make(config: Config): Validator = new TestingValidatorSPI(Seq.empty,
Seq.empty)
+}
+object PassingValidator {
+ val name = "passing-validator"
+}
+
+class FailingValidatorFactory extends ValidatorFactory {
+ def name(): String = FailingValidator.name
+ def make(config: Config): Validator = new TestingValidatorSPI(Seq.empty,
Seq(ValFail("boom")))
+}
+object FailingValidator {
+ val name = "failing-validator"
+}
+
+class TestingValidatorSPI(w: Seq[ValidationWarning], f:
Seq[ValidationFailure]) extends Validator {
+ def validateXML(document: InputStream): ValidationResult =
ValidationResult(w, f)
+}
+
+case class ValFail(getMessage: String) extends ValidationFailure
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
index 6e79368..3d60450 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
@@ -31,8 +31,13 @@ import java.util.zip.GZIPOutputStream
import scala.collection.immutable.Queue
import scala.collection.mutable
-
-import org.apache.daffodil.Implicits._; object INoWarn4 {
+import org.apache.daffodil.Implicits._
+import org.apache.daffodil.api.ValidationException
+import org.apache.daffodil.api.ValidationFailure
+import org.apache.daffodil.api.ValidationResult
+import org.apache.daffodil.api.Validator
+import org.apache.daffodil.validation.XercesValidatorFactory
+; object INoWarn4 {
ImplicitsSuppressUnusedImportWarning() }
import org.apache.daffodil.api.DFDL
import org.apache.daffodil.api.DaffodilTunables
@@ -50,7 +55,6 @@ import org.apache.daffodil.exceptions.UnsuppressableException
import org.apache.daffodil.externalvars.Binding
import org.apache.daffodil.externalvars.ExternalVariablesLoader
import org.apache.daffodil.infoset.DIElement
-import org.apache.daffodil.infoset.InfosetElement
import org.apache.daffodil.infoset.InfosetException
import org.apache.daffodil.infoset.InfosetInputter
import org.apache.daffodil.infoset.InfosetOutputter
@@ -70,14 +74,12 @@ import org.apache.daffodil.processors.unparsers.UnparseError
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe._
import org.apache.daffodil.util.Misc
-import org.apache.daffodil.util.Validator
import org.apache.daffodil.xml.XMLUtils
import org.xml.sax.ContentHandler
import org.xml.sax.DTDHandler
import org.xml.sax.EntityResolver
import org.xml.sax.ErrorHandler
import org.xml.sax.InputSource
-import org.xml.sax.SAXException
import org.xml.sax.SAXNotRecognizedException
import org.xml.sax.SAXNotSupportedException
import org.xml.sax.SAXParseException
@@ -241,6 +243,17 @@ class DataProcessor private (
def withValidationMode(mode:ValidationMode.Type): DataProcessor =
copy(validationMode = mode)
+ def withValidator(validator: Validator): DataProcessor =
withValidationMode(ValidationMode.Custom(validator))
+
+ lazy val validator: Validator = {
+ validationMode match {
+ case ValidationMode.Custom(cv) => cv
+ case _ =>
+ val cfg =
XercesValidatorFactory.makeConfig(ssrd.elementRuntimeData.schemaURIStringsForFullValidation)
+ XercesValidatorFactory.makeValidator(cfg)
+ }
+ }
+
// TODO Deprecate and replace usages with just tunables.
def getTunables: DaffodilTunables = tunables
@@ -416,14 +429,16 @@ class DataProcessor private (
// events are created rather than writing the entire infoset in memory and
// then validating at the end of the parse. See DAFFODIL-2386
//
- val (outputter, maybeValidationBytes) =
- if (validationMode == ValidationMode.Full) {
- val bos = new java.io.ByteArrayOutputStream()
- val xmlOutputter = new XMLTextInfosetOutputter(bos, false)
- val teeOutputter = new TeeInfosetOutputter(output, xmlOutputter)
- (teeOutputter, One(bos))
- } else {
- (output, Nope)
+ val (outputter, maybeValidationBytes) = {
+ validationMode match {
+ case ValidationMode.Full | ValidationMode.Custom(_) =>
+ val bos = new java.io.ByteArrayOutputStream()
+ val xmlOutputter = new XMLTextInfosetOutputter(bos, false)
+ val teeOutputter = new TeeInfosetOutputter(output, xmlOutputter)
+ (teeOutputter, One(bos))
+ case _ =>
+ (output, Nope)
+ }
}
val rootERD = ssrd.elementRuntimeData
@@ -688,41 +703,28 @@ class DataProcessor private (
class ParseResult(dp: DataProcessor, override val resultState: PState)
extends DFDL.ParseResult
- with WithDiagnosticsImpl
- with ErrorHandler {
+ with WithDiagnosticsImpl {
/**
- * To be successful here, we need to capture xerces parse/validation
+ * To be successful here, we need to capture parse/validation
* errors and add them to the Diagnostics list in the PState.
*
- * @param state the initial parse state.
+ * @param bytes the parsed Infoset
*/
def validateResult(bytes: Array[Byte]): Unit = {
Assert.usage(resultState.processorStatus eq Success)
- val schemaURIStrings =
resultState.infoset.asInstanceOf[InfosetElement].runtimeData.schemaURIStringsForFullValidation
- try {
- val bis = new java.io.ByteArrayInputStream(bytes)
- Validator.validateXMLSources(schemaURIStrings, bis, this)
- } catch {
- //
- // Some SAX Parse errors are thrown even if you specify an error handler
to the
- // validator.
- //
- // So we also need this catch
- //
- case e: SAXException =>
- resultState.validationErrorNoContext(e)
- }
- }
- override def warning(spe: SAXParseException): Unit = {
- resultState.validationErrorNoContext(spe)
- }
- override def error(spe: SAXParseException): Unit = {
- resultState.validationErrorNoContext(spe)
- }
- override def fatalError(spe: SAXParseException): Unit = {
- resultState.validationErrorNoContext(spe)
+ val bis = new java.io.ByteArrayInputStream(bytes)
+ dp.validator.validateXML(bis) match {
+ case ValidationResult(warnings, errors) =>
+ warnings.forEach{ w => resultState.validationError(w.getMessage) }
+ errors.forEach{
+ case e: ValidationException =>
+ resultState.validationErrorNoContext(e.getCause)
+ case f: ValidationFailure =>
+ resultState.validationError(f.getMessage)
+ }
+ }
}
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
index 09bc00c..da76ce8 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
@@ -672,7 +672,7 @@ sealed class ElementRuntimeData(
def isSimpleType = optPrimType.isDefined
- def schemaURIStringsForFullValidation =
schemaURIStringsForFullValidation1.distinct
+ lazy val schemaURIStringsForFullValidation =
schemaURIStringsForFullValidation1.distinct
private def schemaURIStringsForFullValidation1: Seq[String] =
(schemaFileLocation.uriString +:
childERDs.flatMap { _.schemaURIStringsForFullValidation1 })
diff --git
a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/Daffodil.scala
b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/Daffodil.scala
index 19fb3ec..0d54ded 100644
--- a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/Daffodil.scala
+++ b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/Daffodil.scala
@@ -55,6 +55,7 @@ import org.apache.daffodil.processors.{ InvalidUsageException
=> SInvalidUsageEx
import java.net.URI
import org.apache.daffodil.api.URISchemaSource
+import org.apache.daffodil.api.Validator
import org.apache.daffodil.sapi.ValidationMode.ValidationMode
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe._
@@ -105,6 +106,10 @@ object ValidationMode extends Enumeration {
val Off = Value(10)
val Limited = Value(20)
val Full = Value(30)
+
+ case class Custom(v: Validator) extends ValidationMode {
+ val id: Int = 100
+ }
}
/**
@@ -563,6 +568,8 @@ class DataProcessor private[sapi] (private var dp:
SDataProcessor)
catch { case e: SInvalidUsageException => throw new
InvalidUsageException(e) }
}
+ def withValidator(validator: Validator): DataProcessor =
withValidationMode(ValidationMode.Custom(validator))
+
/**
* Read external variables from a Daffodil configuration file
diff --git
a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/packageprivate/Utils.scala
b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/packageprivate/Utils.scala
index 24eb415..2f66d73 100644
---
a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/packageprivate/Utils.scala
+++
b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/packageprivate/Utils.scala
@@ -71,6 +71,7 @@ private[sapi] object ValidationConversions {
case ValidationMode.Off => SValidationMode.Off
case ValidationMode.Limited => SValidationMode.Limited
case ValidationMode.Full => SValidationMode.Full
+ case ValidationMode.Custom(v) => SValidationMode.Custom(v)
}
smode
}
@@ -80,6 +81,7 @@ private[sapi] object ValidationConversions {
case SValidationMode.Off => ValidationMode.Off
case SValidationMode.Limited => ValidationMode.Limited
case SValidationMode.Full => ValidationMode.Full
+ case SValidationMode.Custom(v) => ValidationMode.Custom(v)
}
mode
}
diff --git
a/daffodil-sapi/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
b/daffodil-sapi/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
new file mode 100644
index 0000000..9567de5
--- /dev/null
+++
b/daffodil-sapi/src/test/resources/META-INF/services/org.apache.daffodil.api.ValidatorFactory
@@ -0,0 +1,17 @@
+# 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.
+#
+org.apache.daffodil.example.PassingValidatorFactory
+org.apache.daffodil.example.FailingValidatorFactory
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorApiExample.scala
similarity index 51%
copy from
daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
copy to
daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorApiExample.scala
index 3dc7a65..2061634 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/ValidationMode.scala
+++
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorApiExample.scala
@@ -15,15 +15,28 @@
* limitations under the License.
*/
-package org.apache.daffodil.api
+package org.apache.daffodil.example
-import org.apache.daffodil.util.Enum
+import org.junit.Test
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
-object ValidationMode extends Enum {
- sealed abstract class Type protected (val mode: Int) extends EnumValueType
with Ordered[Type] with Serializable {
- def compare(that: ValidationMode.Type) = this.mode - that.mode
+class ValidatorApiExample extends ValidatorExamplesSupport {
+ @Test
+ def testAlwaysPass(): Unit =
+ withSchema("/test/sapi/mySchema5.dfdl.xsd") { dp =>
+ withInput("/test/sapi/myData5.dat") { input =>
+ val res = dp.withValidator(Always.passes).parse(input, `/dev/null`)
+ assertFalse(res.isValidationError())
+ }
}
- case object Off extends Type(10)
- case object Limited extends Type(20)
- case object Full extends Type(30)
+
+ @Test
+ def testAlwaysFail(): Unit =
+ withSchema("/test/sapi/mySchema5.dfdl.xsd") { dp =>
+ withInput("/test/sapi/myData5.dat") { input =>
+ val res = dp.withValidator(Always.fails).parse(input, `/dev/null`)
+ assertTrue(res.isValidationError())
+ }
+ }
}
diff --git
a/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorExamplesSupport.scala
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorExamplesSupport.scala
new file mode 100644
index 0000000..a9622e4
--- /dev/null
+++
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorExamplesSupport.scala
@@ -0,0 +1,95 @@
+/*
+ * 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.example
+
+import java.io.File
+import java.io.FileInputStream
+import java.io.InputStream
+
+import com.typesafe.config.Config
+import org.apache.daffodil.api.ValidationFailure
+import org.apache.daffodil.api.ValidationResult
+import org.apache.daffodil.api.ValidationWarning
+import org.apache.daffodil.api.Validator
+import org.apache.daffodil.api.ValidatorFactory
+import org.apache.daffodil.sapi.Daffodil
+import org.apache.daffodil.sapi.DataProcessor
+import org.apache.daffodil.sapi.infoset.NullInfosetOutputter
+import org.apache.daffodil.sapi.io.InputSourceDataInputStream
+
+abstract class ValidatorExamplesSupport {
+ private def fileFromResource(path: String): File = new
File(getClass.getResource(path).toURI)
+ val `/dev/null` = new NullInfosetOutputter()
+
+ def withSchema(name: String)(f: DataProcessor => Unit): Unit = {
+ val c = Daffodil.compiler()
+ val schemaFile = fileFromResource(name)
+ val pf = c.compileFile(schemaFile)
+ f(pf.onPath("/"))
+ }
+
+ def withInput(name: String)(f: InputSourceDataInputStream => Unit): Unit = {
+ f(new InputSourceDataInputStream(new
FileInputStream(fileFromResource(name))))
+ }
+}
+
+class CustomValidator extends Validator {
+ def validateXML(document: InputStream): ValidationResult =
+ ValidationResult.empty
+}
+
+class CustomValidatorFactory extends ValidatorFactory {
+ def name(): String = "sapi-custom"
+ def make(config: Config): Validator = new CustomValidator
+}
+
+class AlwaysValidator(w: Seq[ValidationWarning], e: Seq[ValidationFailure])
extends Validator {
+ def validateXML(document: InputStream): ValidationResult =
+ ValidationResult(w, e)
+}
+
+object Boom extends ValidationFailure {
+ def getMessage: String = "boom"
+}
+
+object Always {
+ def fails: Validator = (_: InputStream) => ValidationResult(Seq.empty,
Seq(Boom))
+ def passes: Validator = (_: InputStream) => ValidationResult.empty
+}
+
+class PassingValidatorFactory extends ValidatorFactory {
+ def name(): String = PassingValidator.name
+ def make(config: Config): Validator = new TestingValidatorSPI(Seq.empty,
Seq.empty)
+}
+object PassingValidator {
+ val name = "passing-validator"
+}
+
+class FailingValidatorFactory extends ValidatorFactory {
+ def name(): String = FailingValidator.name
+ def make(config: Config): Validator = new TestingValidatorSPI(Seq.empty,
Seq(ValFail("boom")))
+}
+object FailingValidator {
+ val name = "failing-validator"
+}
+
+class TestingValidatorSPI(w: Seq[ValidationWarning], f:
Seq[ValidationFailure]) extends Validator {
+ def validateXML(document: InputStream): ValidationResult =
ValidationResult(w, f)
+}
+
+case class ValFail(getMessage: String) extends ValidationFailure
diff --git
a/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorSpiExample.scala
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorSpiExample.scala
new file mode 100644
index 0000000..4afd974
--- /dev/null
+++
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/ValidatorSpiExample.scala
@@ -0,0 +1,48 @@
+/*
+ * 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.example
+
+import com.typesafe.config.ConfigFactory
+import org.apache.daffodil.validation.Validators
+import org.junit.Test
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+
+// there is no support for passing validators by name into the dp as of yet
+// so these tests simply load via spi a validator and pass it into the SAPI
+class ValidatorSpiExample extends ValidatorExamplesSupport {
+ @Test
+ def testAlwaysPass(): Unit =
+ withSchema("/test/sapi/mySchema5.dfdl.xsd") { dp =>
+ withInput("/test/sapi/myData5.dat") { input =>
+ val v = Validators.get(PassingValidator.name).make(ConfigFactory.empty)
+ val res = dp.withValidator(v).parse(input, `/dev/null`)
+ assertFalse(res.isValidationError())
+ }
+ }
+
+ @Test
+ def testAlwaysFail(): Unit =
+ withSchema("/test/sapi/mySchema5.dfdl.xsd") { dp =>
+ withInput("/test/sapi/myData5.dat") { input =>
+ val v = Validators.get(FailingValidator.name).make(ConfigFactory.empty)
+ val res = dp.withValidator(v).parse(input, `/dev/null`)
+ assertTrue(res.isValidationError())
+ }
+ }
+}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 91a7ebb..fd076c7 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -29,6 +29,7 @@ object Dependencies {
"xml-resolver" % "xml-resolver" % "1.2",
"commons-io" % "commons-io" % "2.8.0",
"jline" % "jline" % "2.14.6",
+ "com.typesafe" % "config" % "1.4.0"
)
lazy val infoset = Seq(