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 02e0623 Add dfdlx:runtimeProperties extension
02e0623 is described below
commit 02e0623b245171c74677169050590cd8344e66bf
Author: Steve Lawrence <[email protected]>
AuthorDate: Thu Sep 2 07:27:04 2021 -0400
Add dfdlx:runtimeProperties extension
New dfdlx:runtimeProperties is a space-separated list of key=value
pairs. The pairs are converted to a Map and made available on the
ElementRuntimeData for which the properties is specified. This Map can
be accessed by custom implementations of InfosetInputters and
InfosetOutputters to allow modifications and custom behaviors to the
text of simple types.
The property is only valid on dfdl:element and xs:element tags for
simple types.
Also changed "Object" to "ObjectKind" to make it more clear that it
describes the dfdlx:objectKind extension property.
DAFFODIL-2537
---
.../org/apache/daffodil/dsom/ElementBase.scala | 53 +++--
.../runtime1/ElementBaseRuntime1Mixin.scala | 3 +-
.../daffodil/general/TestRuntimeProperties.scala | 231 +++++++++++++++++++++
.../processor/TestSAXParseUnparseAPI.scala | 2 +-
.../org/apache/daffodil/japi/infoset/Infoset.scala | 17 +-
.../apache/daffodil/example/TestInfosetEvent.java | 93 +++++++++
.../daffodil/example/TestInfosetInputter.java | 86 ++++++++
.../daffodil/example/TestInfosetOutputter.java | 103 +++++++++
.../org/apache/daffodil/example/TestJavaAPI.java | 38 ++++
.../apache/daffodil/xsd/DFDL_part2_attributes.xsd | 3 +-
.../org/apache/daffodil/xsd/DFDL_part3_model.xsd | 3 +-
.../resources/org/apache/daffodil/xsd/dfdlx.xsd | 29 ++-
.../daffodil/propGen/PropertyGenerator.scala | 11 +-
.../apache/daffodil/infoset/InfosetInputter.scala | 4 +-
.../daffodil/infoset/JDOMInfosetInputter.scala | 2 +-
.../daffodil/infoset/JsonInfosetInputter.scala | 2 +-
.../daffodil/infoset/SAXInfosetInputter.scala | 2 +-
.../daffodil/infoset/ScalaXMLInfosetInputter.scala | 2 +-
.../daffodil/infoset/W3CDOMInfosetInputter.scala | 2 +-
.../daffodil/infoset/XMLTextInfosetInputter.scala | 2 +-
.../apache/daffodil/processors/RuntimeData.scala | 6 +-
.../org/apache/daffodil/sapi/infoset/Infoset.scala | 17 +-
.../example/TestInfosetInputterOutputter.scala | 145 +++++++++++++
.../org/apache/daffodil/example/TestScalaAPI.scala | 42 ++++
.../apache/daffodil/tdml/TDMLInfosetInputter.scala | 6 +-
25 files changed, 866 insertions(+), 38 deletions(-)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
index fa675e1..36dd7dc 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
@@ -17,27 +17,27 @@
package org.apache.daffodil.dsom
-import org.apache.daffodil.equality._
-import org.apache.daffodil.processors._
-import org.apache.daffodil.schema.annotation.props._
-import org.apache.daffodil.xml._
-import org.apache.daffodil.grammar.ElementBaseGrammarMixin
-import org.apache.daffodil.schema.annotation.props.gen._
-import org.apache.daffodil.util.Misc
+import java.lang.{ Integer => JInt }
import scala.xml.NamespaceBinding
-import org.apache.daffodil.util.MaybeULong
+
+import org.apache.daffodil.api.WarnID
+import org.apache.daffodil.dpath.InvalidPrimitiveDataException
import org.apache.daffodil.dpath.NodeInfo
import org.apache.daffodil.dpath.NodeInfo.PrimType
-import org.apache.daffodil.dpath.InvalidPrimitiveDataException
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.api.WarnID
-import java.lang.{ Integer => JInt }
-
import org.apache.daffodil.dsom.walker.ElementBaseView
+import org.apache.daffodil.equality._
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.grammar.ElementBaseGrammarMixin
import org.apache.daffodil.infoset.DataValue
import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
import
org.apache.daffodil.infoset.DataValue.DataValuePrimitiveOrUseNilForDefaultOrNull
+import org.apache.daffodil.processors._
+import org.apache.daffodil.schema.annotation.props._
+import org.apache.daffodil.schema.annotation.props.gen._
+import org.apache.daffodil.util.MaybeULong
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.xml._
/**
* Note about DSOM design versus say XSOM or Apache XSD library.
@@ -1179,4 +1179,31 @@ trait ElementBase
Nil
res
}
+
+ /**
+ * This runtimeProperties is just a map of key value pairs that can be
+ * applied to simple elements. Each runtime can choose how to use thes key
+ * value pairs as they wish. The property is currently limited to simple
+ * types since the only current use case relates to the ability to modify the
+ * value of simple type values in the runtime1 InfosetInputter/Outputter.
+ * This restriction may be lifted in the future. Note that we create a Java
+ * map here since this will be directly provied to the Java API. This avoids
+ * some potential asJava overhead, but is still easily usable in the Scala
+ * API
+ */
+ lazy val runtimeProperties: java.util.Map[String,String] =
findPropertyOption("runtimeProperties").toOption match {
+ case None => java.util.Collections.emptyMap()
+ case Some(value) => {
+ schemaDefinitionUnless(isSimpleType, "dfdlx:runtimeProperties is only
valid on simple types")
+ val map = new java.util.HashMap[String,String]()
+ value.split("\\s+").filter(_ != "").foreach { v =>
+ val kv = v.split("=")
+ // DFDL schema validation should ensure that this is valid syntax, but
double check to be safe
+ schemaDefinitionUnless(kv.length == 2 && kv(0).length > 0 &&
kv(1).length > 0,
+ "dfdlx:runtimeProperties must be a space-separated list of
\"key=value\" pairs: \"%s\" is invalid", v)
+ map.put(kv(0), kv(1))
+ }
+ map
+ }
+ }
}
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/ElementBaseRuntime1Mixin.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/ElementBaseRuntime1Mixin.scala
index 1bada66..f81e277 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/ElementBaseRuntime1Mixin.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/ElementBaseRuntime1Mixin.scala
@@ -209,7 +209,8 @@ trait ElementBaseRuntime1Mixin { self: ElementBase =>
fillByteEv,
maybeCheckByteAndBitOrderEv,
maybeCheckBitOrderAndCharsetEv,
- isQuasiElement)
+ isQuasiElement,
+ runtimeProperties)
newERD
}.value
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/general/TestRuntimeProperties.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/general/TestRuntimeProperties.scala
new file mode 100644
index 0000000..77eb8bf
--- /dev/null
+++
b/daffodil-core/src/test/scala/org/apache/daffodil/general/TestRuntimeProperties.scala
@@ -0,0 +1,231 @@
+/*
+ * 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.general
+
+import org.junit.Test
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+
+import org.apache.daffodil.Implicits.intercept
+import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.infoset.DISimple
+import org.apache.daffodil.infoset.ScalaXMLInfosetInputter
+import org.apache.daffodil.infoset.ScalaXMLInfosetOutputter
+import org.apache.daffodil.io.InputSourceDataInputStream
+import org.apache.daffodil.util.SchemaUtils
+import org.apache.daffodil.util.TestUtils
+
+/**
+ * Test InfosetOutputter that extends the ScalaXMLInfosetOutputter and redacts
+ * words specified in the dfdlx:runtimeProperties extension
+ */
+class RedactingScalaXMLInfosetOutputter
+ extends ScalaXMLInfosetOutputter {
+
+ override def startSimple(diSimple: DISimple): Boolean = {
+ super.startSimple(diSimple)
+
+ val runtimeProperties = diSimple.erd.runtimeProperties
+
+ val redactions = Option(runtimeProperties.get("redact")).map { value =>
value.split(",") }
+ if (redactions.isDefined) {
+ val thisElem = stack.top.last.asInstanceOf[scala.xml.Elem]
+ var text = thisElem.child(0).asInstanceOf[scala.xml.Text].data
+ val redactWord = runtimeProperties.getOrDefault("redactWord", "")
+ redactions.get.foreach { redaction =>
+ text = text.replace(redaction, redactWord)
+ }
+ val newChild = Seq(new scala.xml.Text(text))
+ val newElem = thisElem.copy(child = newChild)
+ stack.top(stack.top.size - 1) = newElem
+ }
+ true
+ }
+}
+
+/**
+ * Test InfosetInputter that extends the ScalaXMLInfosetInputter and redacts
+ * words specified in the dfdlx:runtimeProperties extension
+ */
+class RedactingScalaXMLInfosetInputter(rootNode: scala.xml.Node)
+ extends ScalaXMLInfosetInputter(rootNode) {
+
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
+ var text = super.getSimpleText(primType, runtimeProperties)
+
+ val redactions = Option(runtimeProperties.get("redact")).map { value =>
value.split(",") }
+ if (redactions.isDefined) {
+ val redactWord = runtimeProperties.getOrDefault("redactWord", "")
+ redactions.get.foreach { redaction =>
+ text = text.replace(redaction, redactWord)
+ }
+ }
+ text
+ }
+}
+
+class TestRuntimeProperties {
+
+ val testSchema1 = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+ <dfdl:format ref="tns:GeneralFormat" lengthKind="delimited"/>,
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence dfdl:separator=",">
+ <xs:element name="filtered" type="xs:string"
dfdlx:runtimeProperties="redact=foo,baz redactWord=REDACTED" />
+ <xs:element name="unFiltered" type="xs:string" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ )
+
+ @Test def testRuntimeProperties_01(): Unit = {
+
+ val data = "foo bar baz,foo bar baz"
+ val expected =
+ <root xmlns="http://example.com">
+ <filtered>REDACTED bar REDACTED</filtered>
+ <unFiltered>foo bar baz</unFiltered>
+ </root>
+
+ val input = InputSourceDataInputStream(data.getBytes("UTF-8"))
+ val outputter = new RedactingScalaXMLInfosetOutputter()
+
+ val dp = TestUtils.compileSchema(testSchema1)
+ val pr = dp.parse(input, outputter)
+
+ assertFalse(pr.isError)
+ val actual = outputter.getResult()
+ TestUtils.assertEqualsXMLElements(expected, actual)
+ }
+
+ @Test def testRuntimeProperties_02(): Unit = {
+
+ val infoset =
+ <root xmlns="http://example.com">
+ <filtered>foo bar baz</filtered>
+ <unFiltered>foo bar baz</unFiltered>
+ </root>
+ val expected = "REDACTED bar REDACTED,foo bar baz"
+
+ val inputter = new RedactingScalaXMLInfosetInputter(infoset)
+ val output = new java.io.ByteArrayOutputStream()
+
+ val dp = TestUtils.compileSchema(testSchema1)
+ val ur = dp.unparse(inputter, output)
+
+ assertFalse(ur.isError)
+
+ val actual = output.toString("UTF-8")
+ assertEquals(expected, actual)
+ }
+
+ val testSchemaBad1 = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+ <dfdl:format ref="tns:GeneralFormat" lengthKind="delimited"/>,
+ <xs:element name="root" dfdlx:runtimeProperties="redact=foo,baz">
+ <xs:complexType>
+ <xs:sequence dfdl:separator=",">
+ <xs:element name="filtered" type="xs:string" />
+ <xs:element name="unFiltered" type="xs:string" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ )
+
+ @Test def testRuntimeProperties_03(): Unit = {
+ val ex = intercept[Exception] {
+ TestUtils.compileSchema(testSchemaBad1)
+ }
+ val msg = ex.getMessage()
+
+ assertTrue(msg.contains("Schema Definition Error"))
+ assertTrue(msg.contains("dfdlx:runtimeProperties"))
+ assertTrue(msg.contains("simple type"))
+ }
+
+ val testSchemaBad2 = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+ <dfdl:format ref="tns:GeneralFormat" lengthKind="delimited"/>,
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence dfdl:separator=",">
+ <xs:element name="filtered" type="xs:string"
dfdlx:runtimeProperties="redact=" />
+ <xs:element name="unFiltered" type="xs:string" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ )
+
+ @Test def testRuntimeProperties_04(): Unit = {
+ val ex = intercept[Exception] {
+ TestUtils.compileSchema(testSchemaBad2)
+ }
+ val msg = ex.getMessage()
+
+ assertTrue(msg.contains("Schema Definition Error"))
+ assertTrue(msg.contains("dfdlx:runtimeProperties"))
+ assertTrue(msg.contains("key=value"))
+ }
+
+ val testSchema2 = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+ <dfdl:format ref="tns:GeneralFormat" lengthKind="delimited"/>,
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence dfdl:separator=",">
+ <xs:element name="filtered" type="xs:string">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:element>
+ <dfdl:property name="dfdlx:runtimeProperties">
+ redact=foo,baz
+ redactWord=REDACTED
+ </dfdl:property>
+ </dfdl:element>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="unFiltered" type="xs:string" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ )
+
+ @Test def testRuntimeProperties_05(): Unit = {
+
+ val data = "foo bar baz,foo bar baz"
+ val expected =
+ <root xmlns="http://example.com">
+ <filtered>REDACTED bar REDACTED</filtered>
+ <unFiltered>foo bar baz</unFiltered>
+ </root>
+
+ val input = InputSourceDataInputStream(data.getBytes("UTF-8"))
+ val outputter = new RedactingScalaXMLInfosetOutputter()
+
+ val dp = TestUtils.compileSchema(testSchema2)
+ val pr = dp.parse(input, outputter)
+
+ assertFalse(pr.isError)
+ val actual = outputter.getResult()
+ TestUtils.assertEqualsXMLElements(expected, actual)
+ }
+
+}
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala
index a27f045..e1e07ab 100644
---
a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala
+++
b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala
@@ -195,7 +195,7 @@ class TestSAXParseUnparseAPI {
assertTrue(sii.hasNext())
sii.next()
assertEquals(StartElement, sii.getEventType())
- val stxt = sii.getSimpleText(NodeInfo.String)
+ val stxt = sii.getSimpleText(NodeInfo.String,
java.util.Collections.emptyMap())
assertEquals("&", stxt)
}
}
diff --git
a/daffodil-japi/src/main/scala/org/apache/daffodil/japi/infoset/Infoset.scala
b/daffodil-japi/src/main/scala/org/apache/daffodil/japi/infoset/Infoset.scala
index ad6e612..514f6fb 100644
---
a/daffodil-japi/src/main/scala/org/apache/daffodil/japi/infoset/Infoset.scala
+++
b/daffodil-japi/src/main/scala/org/apache/daffodil/japi/infoset/Infoset.scala
@@ -17,6 +17,7 @@
package org.apache.daffodil.japi.infoset
+import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.infoset.{ InfosetOutputter => SInfosetOutputter }
import org.apache.daffodil.infoset.{ InfosetInputter => SInfosetInputter }
import org.apache.daffodil.infoset.{ ScalaXMLInfosetOutputter =>
SScalaXMLInfosetOutputter }
@@ -74,6 +75,13 @@ abstract class InfosetInputter extends SInfosetInputter {
* NonTextFoundInSimpleContentException. If the element does not have any
* simple content, this should return either null or the empty string.
*/
+ def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String,String]): String =
+ getSimpleText(primType)
+
+ /**
+ * See getSimpleText(primType, runtimeProperties), which has a default
+ * implementation to call this function without the runtimeProperties Map
+ */
def getSimpleText(primType: NodeInfo.Kind): String
/**
@@ -440,7 +448,14 @@ abstract class InfosetInputterProxy extends
InfosetInputter {
override def getEventType() = infosetInputter.getEventType()
override def getLocalName() = infosetInputter.getLocalName()
override def getNamespaceURI() = infosetInputter.getNamespaceURI()
- override def getSimpleText(primType: NodeInfo.Kind) =
infosetInputter.getSimpleText(primType)
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String,String]): String = {
+ infosetInputter.getSimpleText(primType, runtimeProperties)
+ }
+ override def getSimpleText(primType: NodeInfo.Kind) = {
+ //$COVERAGE-OFF$
+ Assert.impossible()
+ //$COVERAGE-ON$
+ }
override def hasNext() = infosetInputter.hasNext()
override def isNilled() = infosetInputter.isNilled()
override def next() = infosetInputter.next()
diff --git
a/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetEvent.java
b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetEvent.java
new file mode 100644
index 0000000..1b7ea6f
--- /dev/null
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetEvent.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+// TODO: Shouldn't need to import things not in the japi package
+import org.apache.daffodil.dpath.NodeInfo;
+import org.apache.daffodil.infoset.InfosetInputterEventType;
+import org.apache.daffodil.infoset.InfosetInputterEventType.EndDocument$;
+import org.apache.daffodil.infoset.InfosetInputterEventType.EndElement$;
+import org.apache.daffodil.infoset.InfosetInputterEventType.StartDocument$;
+import org.apache.daffodil.infoset.InfosetInputterEventType.StartElement$;
+
+
+public class TestInfosetEvent {
+
+ InfosetInputterEventType eventType;
+ String localName;
+ String namespaceURI;
+ String simpleText;
+ // null means no null state specified ( which is required for
+ // non-nullable elements). Boolean.TRUE or Boolean.FALSE means it is
+ // nullable and has the given value
+ Boolean isNilled;
+
+ public TestInfosetEvent(InfosetInputterEventType _eventType, String
_localName, String _namespaceURI, String _simpleText, Boolean _isNilled) {
+ this.eventType = _eventType;
+ this.localName = _localName;
+ this.namespaceURI = _namespaceURI;
+ this.simpleText = _simpleText;
+ this.isNilled = _isNilled;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof TestInfosetEvent)) {
+ return false;
+ }
+ TestInfosetEvent that = (TestInfosetEvent)o;
+ return
+ this.eventType == that.eventType &&
+ java.util.Objects.equals(this.localName, that.localName) &&
+ java.util.Objects.equals(this.namespaceURI, that.namespaceURI) &&
+ java.util.Objects.equals(this.simpleText, that.simpleText) &&
+ java.util.Objects.equals(this.isNilled, that.isNilled);
+
+ }
+
+ static TestInfosetEvent startDocument() {
+ return new TestInfosetEvent (StartDocument$.MODULE$, null, null, null,
null);
+ }
+
+ static TestInfosetEvent startComplex(String name, String namespace) {
+ return startComplex(name, namespace, null);
+ }
+
+ static TestInfosetEvent startComplex(String name, String namespace,
Boolean isNilled) {
+ return new TestInfosetEvent (StartElement$.MODULE$, name, namespace,
null, isNilled);
+ }
+
+ static TestInfosetEvent startSimple(String name, String namespace, String
text) {
+ return startSimple(name, namespace, text, null);
+ }
+
+ static TestInfosetEvent startSimple(String name, String namespace, String
text, Boolean isNilled) {
+ return new TestInfosetEvent (StartElement$.MODULE$, name, namespace,
text, isNilled);
+ }
+
+ static TestInfosetEvent endComplex(String name, String namespace) {
+ return new TestInfosetEvent (EndElement$.MODULE$, name, namespace,
null, null);
+ }
+
+ static TestInfosetEvent endSimple(String name, String namespace) {
+ return new TestInfosetEvent (EndElement$.MODULE$, name, namespace,
null, null);
+ }
+
+ static TestInfosetEvent endDocument() {
+ return new TestInfosetEvent (EndDocument$.MODULE$, null, null, null,
null);
+ }
+}
diff --git
a/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetInputter.java
b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetInputter.java
new file mode 100644
index 0000000..3c62e48
--- /dev/null
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetInputter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.japi.infoset.InfosetInputter;
+
+// TODO: Shouldn't need to import things not in the japi package
+import org.apache.daffodil.dpath.NodeInfo;
+import org.apache.daffodil.infoset.InfosetInputterEventType;
+
+
+public class TestInfosetInputter extends InfosetInputter {
+
+ private TestInfosetEvent[] events;
+ private int curEventIndex;
+
+ TestInfosetInputter(TestInfosetEvent... _events) {
+ events = _events;
+ curEventIndex = 0;
+ }
+
+ @Override
+ public InfosetInputterEventType getEventType() {
+ return events[curEventIndex].eventType;
+ }
+
+ @Override
+ public String getLocalName() {
+ return events[curEventIndex].localName;
+ }
+
+ @Override
+ public String getNamespaceURI() {
+ return events[curEventIndex].namespaceURI;
+ }
+
+ @Override
+ public String getSimpleText(NodeInfo.Kind primType) {
+ return events[curEventIndex].simpleText;
+ }
+
+ // TODO: This should return a MaybeBoolean, but that is Scala value class
+ // with underlying type being an int, so this must return an int. Returning
+ // 1 means true, 0 is false, and -1 is not set
+ @Override
+ public int isNilled() {
+ Boolean isNilled = events[curEventIndex].isNilled;
+ if (isNilled == null) { return -1; }
+ else return isNilled ? 1 : 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return curEventIndex + 1 < this.events.length;
+ }
+
+ @Override
+ public void next() {
+ curEventIndex++;
+ }
+
+ @Override
+ public boolean supportsNamespaces() {
+ return true;
+ }
+
+ @Override
+ public void fini() {
+ // noop
+ }
+}
diff --git
a/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetOutputter.java
b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetOutputter.java
new file mode 100644
index 0000000..ed1658e
--- /dev/null
+++
b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestInfosetOutputter.java
@@ -0,0 +1,103 @@
+/*
+ * 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.util.ArrayList;
+
+import org.apache.daffodil.japi.infoset.InfosetOutputter;
+
+// TODO: Shouldn't need to import things not in the japi package
+import org.apache.daffodil.infoset.DIArray;
+import org.apache.daffodil.infoset.DIComplex;
+import org.apache.daffodil.infoset.DISimple;
+
+
+public class TestInfosetOutputter extends InfosetOutputter {
+
+ public ArrayList<TestInfosetEvent> events;
+
+ TestInfosetOutputter() {
+ events = new ArrayList<>();
+ }
+
+ @Override
+ public void reset() {
+ events = new ArrayList<>();
+ }
+
+ @Override
+ public boolean startDocument() {
+ events.add(TestInfosetEvent.startDocument());
+ return true;
+ }
+
+ @Override
+ public boolean endDocument() {
+ events.add(TestInfosetEvent.endDocument());
+ return true;
+ }
+
+ @Override
+ public boolean startSimple(DISimple diSimple) {
+ events.add(
+ TestInfosetEvent.startSimple(
+ diSimple.erd().name(),
+ diSimple.erd().namedQName().namespace().toString(),
+ diSimple.dataValueAsString(),
+ diSimple.erd().isNillable() ? diSimple.isNilled() : null));
+ return true;
+ }
+
+ @Override
+ public boolean endSimple(DISimple diSimple) {
+ events.add(
+ TestInfosetEvent.endSimple(
+ diSimple.erd().name(),
+ diSimple.erd().namedQName().namespace().toString()));
+ return true;
+ }
+
+ @Override
+ public boolean startComplex(DIComplex diComplex) {
+ events.add(
+ TestInfosetEvent.startComplex(
+ diComplex.erd().name(),
+ diComplex.erd().namedQName().namespace().toString(),
+ diComplex.erd().isNillable() ? diComplex.isNilled() : null));
+ return true;
+ }
+
+ @Override
+ public boolean endComplex(DIComplex diComplex) {
+ events.add(
+ TestInfosetEvent.endComplex(
+ diComplex.erd().name(),
+ diComplex.erd().namedQName().namespace().toString()));
+ return true;
+ }
+
+ @Override
+ public boolean startArray(DIArray diArray) {
+ return true;
+ }
+
+ @Override
+ public boolean endArray(DIArray diArray) {
+ return true;
+ }
+}
diff --git
a/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java
b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java
index 026c203..a506918 100644
--- a/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java
+++ b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java
@@ -17,6 +17,7 @@
package org.apache.daffodil.example;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -1167,4 +1168,41 @@ public class TestJavaAPI {
}
}
+ @Test
+ public void testJavaAPI25() throws IOException, ClassNotFoundException,
ExternalVariableException {
+ // Demonstrates the use of a custom InfosetInputter/Outputter
+
+ String expectedData = "42";
+ TestInfosetEvent expectedEvents[] = {
+ TestInfosetEvent.startDocument(),
+ TestInfosetEvent.startComplex("e1", "http://example.com"),
+ TestInfosetEvent.startSimple("e2", "http://example.com",
expectedData),
+ TestInfosetEvent.endSimple("e2", "http://example.com"),
+ TestInfosetEvent.endComplex("e1", "http://example.com"),
+ TestInfosetEvent.endDocument()
+ };
+
+ 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("/");
+
+ java.io.File file = getResource("/test/japi/myData.dat");
+ java.io.FileInputStream fis = new java.io.FileInputStream(file);
+ InputSourceDataInputStream dis = new InputSourceDataInputStream(fis);
+ TestInfosetOutputter outputter = new TestInfosetOutputter();
+ ParseResult pr = dp.parse(dis, outputter);
+
+ assertFalse(pr.isError());
+ assertArrayEquals(expectedEvents, outputter.events.toArray());
+
+ java.io.ByteArrayOutputStream bos = new
java.io.ByteArrayOutputStream();
+ java.nio.channels.WritableByteChannel wbc =
java.nio.channels.Channels.newChannel(bos);
+ TestInfosetInputter inputter = new TestInfosetInputter(expectedEvents);
+ UnparseResult ur = dp.unparse(inputter, wbc);
+
+ assertFalse(ur.isError());
+ assertEquals(expectedData, bos.toString());
+ }
+
}
diff --git
a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd
b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd
index 781f75c..b0d14d0 100644
---
a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd
+++
b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd
@@ -1036,6 +1036,7 @@
<xsd:attributeGroup ref="dfdl:DefaultValueControlAGQualified" />
<xsd:attributeGroup ref="dfdl:OccursAGQualified" />
<xsd:attributeGroup ref="dfdl:CalculatedValueAGQualified" />
+ <xsd:attributeGroup ref="dfdlx:RuntimePropertiesAGQualified" />
</xsd:attributeGroup>
<!-- dfdl:group takes the union of dfdl:sequence and dfdl:choice properties
-->
@@ -1066,7 +1067,7 @@
<xsd:attributeGroup ref="dfdl:BooleanTextAGQualified" />
<xsd:attributeGroup ref="dfdl:BooleanBinaryAGQualified" />
<xsd:attributeGroup ref="dfdlx:SimpleTypeValueCalcAGQualified" />
- <xsd:attributeGroup ref="dfdlx:ObjectAGQualified" />
+ <xsd:attributeGroup ref="dfdlx:ObjectKindAGQualified" />
</xsd:attributeGroup>
<xsd:attributeGroup name="LayeringAGQualified">
diff --git
a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd
b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd
index f3c15b6..c182782 100644
---
a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd
+++
b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part3_model.xsd
@@ -328,6 +328,7 @@
<xsd:attributeGroup ref="dfdl:NillableAG" />
<xsd:attributeGroup ref="dfdl:DefaultValueControlAG" />
<xsd:attributeGroup ref="dfdl:OccursAG" />
+ <xsd:attributeGroup ref="dfdlx:RuntimePropertiesAG" />
</xsd:attributeGroup>
<xsd:attributeGroup name="ElementAG">
@@ -363,7 +364,7 @@
<xsd:attributeGroup ref="dfdl:BooleanTextAG" />
<xsd:attributeGroup ref="dfdl:BooleanBinaryAG" />
<xsd:attributeGroup ref="dfdlx:SimpleTypeValueCalcAG" />
- <xsd:attributeGroup ref="dfdlx:ObjectAG" />
+ <xsd:attributeGroup ref="dfdlx:ObjectKindAG" />
</xsd:attributeGroup>
</xsd:schema>
diff --git
a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
index d253767..416db31 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
@@ -45,6 +45,7 @@
<xs:enumeration value="dfdlx:repType"/>
<xs:enumeration value="dfdlx:repValueRanges"/>
<xs:enumeration value="dfdlx:repValues"/>
+ <xs:enumeration value="dfdlx:runtimeProperties"/>
</xs:restriction>
</xs:simpleType>
@@ -129,11 +130,11 @@
<xs:attribute form="qualified" name="layerBoundaryMark"
type="dfdl:ListOfDFDLStringLiteral_Or_DFDLExpression" />
</xs:attributeGroup>
- <xs:attributeGroup name="ObjectAG">
- <xs:attribute form="qualified" name="ObjectKind"
type="dfdlx:ObjectKindType" />
+ <xs:attributeGroup name="ObjectKindAG">
+ <xs:attribute form="qualified" name="objectKind"
type="dfdlx:ObjectKindType" />
</xs:attributeGroup>
- <xs:attributeGroup name="ObjectAGQualified">
+ <xs:attributeGroup name="ObjectKindAGQualified">
<xs:attribute form="qualified" name="objectKind"
type="dfdlx:ObjectKindType" />
</xs:attributeGroup>
@@ -144,4 +145,26 @@
</xs:restriction>
</xs:simpleType>
+ <xs:attributeGroup name="RuntimePropertiesAG">
+ <xs:attribute form="qualified" name="runtimeProperties"
type="dfdlx:RuntimePropertiesType" />
+ </xs:attributeGroup>
+
+ <xs:attributeGroup name="RuntimePropertiesAGQualified">
+ <xs:attribute form="qualified" name="runtimeProperties"
type="dfdlx:RuntimePropertiesType" />
+ </xs:attributeGroup>
+
+ <xs:simpleType name="RuntimePropertiesType">
+ <xs:restriction base="dfdlx:KVList" />
+ </xs:simpleType>
+
+ <xs:simpleType name="KVList">
+ <xs:list>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:pattern value="[a-zA-Z_][a-zA-Z0-9_]*=\S*"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:list>
+ </xs:simpleType>
+
</xs:schema>
diff --git
a/daffodil-propgen/src/main/scala/org/apache/daffodil/propGen/PropertyGenerator.scala
b/daffodil-propgen/src/main/scala/org/apache/daffodil/propGen/PropertyGenerator.scala
index de71526..bed3a7a 100644
---
a/daffodil-propgen/src/main/scala/org/apache/daffodil/propGen/PropertyGenerator.scala
+++
b/daffodil-propgen/src/main/scala/org/apache/daffodil/propGen/PropertyGenerator.scala
@@ -243,9 +243,14 @@ class PropertyGenerator(arg: Node) {
val notFormatProperties = List("ref", "type", "name", "test",
"defaultValue", "message", "baseFormat")
val notScopedFormatProperties = List("inputValueCalc",
"outputValueCalc", "hiddenGroupRef") // do these by-hand since they are not
scoped.
val excludedBecauseDoneByHand =
- List("separatorSuppressionPolicy", "separatorPolicy",
- "textStandardExponentRep", "textStandardExponentCharacter",
- "textOutputMinLength")
+ List(
+ "runtimeProperties",
+ "separatorPolicy",
+ "separatorSuppressionPolicy",
+ "textOutputMinLength",
+ "textStandardExponentCharacter",
+ "textStandardExponentRep",
+ )
val exclusions = notFormatProperties ++ notScopedFormatProperties ++
excludedBecauseDoneByHand
if (exclusions.contains(rawName)) {
Nil
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetInputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetInputter.scala
index 0c3dff1..6752e23 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetInputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetInputter.scala
@@ -121,7 +121,7 @@ abstract class InfosetInputter
* NonTextFoundInSimpleContentException. If the element does not have any
* simple content, this should return either null or the empty string.
*/
- def getSimpleText(primType: NodeInfo.Kind): String
+ def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String,String]): String
/**
* Determine if the current event is nilled. This will only be called when
@@ -409,7 +409,7 @@ abstract class InfosetInputter
if (erd.isSimpleType) {
val txt =
try {
- getSimpleText(erd.optPrimType.get)
+ getSimpleText(erd.optPrimType.get, erd.runtimeProperties)
} catch {
case ex: NonTextFoundInSimpleContentException =>
UnparseError(One(elem.erd.schemaFileLocation), Nope,
ex.getMessage())
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetInputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetInputter.scala
index fea24c7..f6c77af 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetInputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetInputter.scala
@@ -81,7 +81,7 @@ class JDOMInfosetInputter(doc: Document)
override def getNamespaceURI(): String = stack.top._1.getNamespace.getURI
- override def getSimpleText(primType: NodeInfo.Kind): String = {
+ override def getSimpleText(primType: NodeInfo.Kind, runtimePropertes:
java.util.Map[String, String]): String = {
val text =
if (stack.top._2.hasNext) {
val child = stack.top._2.next
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JsonInfosetInputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JsonInfosetInputter.scala
index 0367c64..1fd28c2 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JsonInfosetInputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JsonInfosetInputter.scala
@@ -120,7 +120,7 @@ class JsonInfosetInputter private (input:
Either[java.io.Reader, java.io.InputSt
null
}
- override def getSimpleText(primType: NodeInfo.Kind): String = {
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
if (jsp.getCurrentToken() == JsonToken.VALUE_NULL) {
null
} else {
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetInputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetInputter.scala
index da3f45d..3ad9bdd 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetInputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetInputter.scala
@@ -90,7 +90,7 @@ class SAXInfosetInputter(
override def getNamespaceURI(): String =
batchedInfosetEvents(currentIndex).namespaceURI.orNull
- override def getSimpleText(primType: NodeInfo.Kind): String = {
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
val res = if (batchedInfosetEvents(currentIndex).simpleText.isDefined) {
batchedInfosetEvents(currentIndex).simpleText.get
} else {
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetInputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetInputter.scala
index c67260d..0fcc68f 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetInputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetInputter.scala
@@ -73,7 +73,7 @@ class ScalaXMLInfosetInputter(rootNode: Node)
override def getNamespaceURI(): String = stack.top._1.namespace
- override def getSimpleText(primType: NodeInfo.Kind): String = {
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
val text = {
val sb = new StringBuilder()
val iter: Iterator[Node] = stack.top._2
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/W3CDOMInfosetInputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/W3CDOMInfosetInputter.scala
index fbeb76c..92c3e37 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/W3CDOMInfosetInputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/W3CDOMInfosetInputter.scala
@@ -84,7 +84,7 @@ class W3CDOMInfosetInputter(doc: Document)
override def getNamespaceURI(): String = stack.top._1.getNamespaceURI
- override def getSimpleText(primType: NodeInfo.Kind): String = {
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
val text =
if (stack.top._2.hasNext) {
val child = stack.top._2.next
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetInputter.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetInputter.scala
index 0f5901d..2fe7a93 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetInputter.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetInputter.scala
@@ -115,7 +115,7 @@ class XMLTextInfosetInputter private (input:
Either[java.io.Reader, java.io.Inpu
xsr.getNamespaceURI()
}
- override def getSimpleText(primType: NodeInfo.Kind): String = {
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
val txt =
try {
xsr.getElementText()
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 31b3746..0637a83 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
@@ -593,7 +593,8 @@ sealed class ElementRuntimeData(
fillByteEvArg: FillByteEv,
maybeCheckByteAndBitOrderEvArg: Maybe[CheckByteAndBitOrderEv],
maybeCheckBitOrderAndCharsetEvArg: Maybe[CheckBitOrderAndCharsetEv],
- val isQuasiElement: Boolean)
+ val isQuasiElement: Boolean,
+ val runtimeProperties: java.util.Map[String,String])
extends TermRuntimeData(positionArg, partialNextElementResolverDelay,
encInfo, dpathElementCompileInfo, isRepresentedArg, couldHaveTextArg,
alignmentValueInBitsArg, hasNoSkipRegionsArg,
defaultBitOrderArg, optIgnoreCaseArg, fillByteEvArg,
@@ -697,7 +698,8 @@ sealed abstract class ErrorERD(local: String, namespaceURI:
String)
null, // fillByteEvArg => FillByteEv
Nope, // maybeCheckByteAndBitOrderEvArg: => Maybe[CheckByteAndBitOrderEv],
Nope, // maybeCheckBitOrderAndCharsetEvArg: =>
Maybe[CheckBitOrderAndCharsetEv],
- false // isQuasiElementArg: => Boolean
+ false, // isQuasiElementArg: => Boolean
+ null, // runtimeProperties: java.util.Map[String,String]
) {
override def toString() = Misc.getNameFromClass(this) + "(" +
this.namedQName.toExtendedSyntax + ")"
diff --git
a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/infoset/Infoset.scala
b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/infoset/Infoset.scala
index 3615329..565785b 100644
---
a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/infoset/Infoset.scala
+++
b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/infoset/Infoset.scala
@@ -17,6 +17,7 @@
package org.apache.daffodil.sapi.infoset
+import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.infoset.{ InfosetOutputter => SInfosetOutputter }
import org.apache.daffodil.infoset.{ InfosetInputter => SInfosetInputter }
import org.apache.daffodil.infoset.{ ScalaXMLInfosetOutputter =>
SScalaXMLInfosetOutputter }
@@ -74,6 +75,13 @@ abstract class InfosetInputter extends SInfosetInputter {
* NonTextFoundInSimpleContentException. If the element does not have any
* simple content, this should return either null or the empty string.
*/
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String =
+ getSimpleText(primType)
+
+ /**
+ * See getSimpleText(primType, runtimeProperties), which has a default
+ * implementation to call this function without the runtimeProperties Map
+ */
def getSimpleText(primType: NodeInfo.Kind): String
/**
@@ -440,7 +448,14 @@ abstract class InfosetInputterProxy extends
InfosetInputter {
override def getEventType() = infosetInputter.getEventType()
override def getLocalName() = infosetInputter.getLocalName()
override def getNamespaceURI() = infosetInputter.getNamespaceURI()
- override def getSimpleText(primType: NodeInfo.Kind) =
infosetInputter.getSimpleText(primType: NodeInfo.Kind)
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
+ infosetInputter.getSimpleText(primType, runtimeProperties)
+ }
+ override def getSimpleText(primType: NodeInfo.Kind) = {
+ //$COVERAGE-OFF$
+ Assert.impossible()
+ //$COVERAGE-ON$
+ }
override def hasNext() = infosetInputter.hasNext()
override def isNilled() = infosetInputter.isNilled()
override def next() = infosetInputter.next()
diff --git
a/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestInfosetInputterOutputter.scala
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestInfosetInputterOutputter.scala
new file mode 100644
index 0000000..249ed9d
--- /dev/null
+++
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestInfosetInputterOutputter.scala
@@ -0,0 +1,145 @@
+/*
+ * 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 scala.collection.mutable.ArrayBuffer
+
+import org.apache.daffodil.sapi.infoset.InfosetInputter
+import org.apache.daffodil.sapi.infoset.InfosetOutputter
+
+// TODO: Shouldn't need to import things not in the sapi package
+import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.infoset.DIArray
+import org.apache.daffodil.infoset.DIComplex
+import org.apache.daffodil.infoset.DISimple
+import org.apache.daffodil.infoset.InfosetInputterEventType
+import org.apache.daffodil.infoset.InfosetInputterEventType.EndDocument
+import org.apache.daffodil.infoset.InfosetInputterEventType.EndElement
+import org.apache.daffodil.infoset.InfosetInputterEventType.StartDocument
+import org.apache.daffodil.infoset.InfosetInputterEventType.StartElement
+import org.apache.daffodil.util.MaybeBoolean
+
+case class TestInfosetEvent(
+ eventType: InfosetInputterEventType,
+ localName: String = null,
+ namespaceURI: String = null,
+ simpleText: String = null,
+ isNilled: MaybeBoolean = MaybeBoolean.Nope,
+)
+
+object TestInfosetEvent {
+
+ def startDocument() =
+ TestInfosetEvent(StartDocument)
+
+ def startComplex(name: String, namespace: String, isNilled: MaybeBoolean =
MaybeBoolean.Nope) =
+ TestInfosetEvent(StartElement, name, namespace, null, isNilled)
+
+ def startSimple(name: String, namespace: String, text: String, isNilled:
MaybeBoolean = MaybeBoolean.Nope) =
+ TestInfosetEvent(StartElement, name, namespace, text, isNilled)
+
+ def endComplex(name: String, namespace: String) =
+ TestInfosetEvent(EndElement, name, namespace)
+
+ def endSimple(name: String, namespace: String) =
+ TestInfosetEvent(EndElement, name, namespace)
+
+ def endDocument() =
+ TestInfosetEvent(EndDocument)
+}
+
+
+case class TestInfosetInputter(events: TestInfosetEvent*) extends
InfosetInputter {
+
+ var curEventIndex = 0
+
+ override def getEventType(): InfosetInputterEventType =
events(curEventIndex).eventType
+ override def getLocalName(): String = events(curEventIndex).localName
+ override def getNamespaceURI(): String = events(curEventIndex).namespaceURI
+ override def getSimpleText(primType: NodeInfo.Kind) =
events(curEventIndex).simpleText
+ override def isNilled(): MaybeBoolean = events(curEventIndex).isNilled
+
+ override def hasNext(): Boolean = curEventIndex + 1 < events.length
+ override def next(): Unit = curEventIndex += 1
+
+ override val supportsNamespaces = true
+
+ override def fini(): Unit = {}
+}
+
+case class TestInfosetOutputter() extends InfosetOutputter {
+
+ val events = new ArrayBuffer[TestInfosetEvent]()
+
+ override def reset(): Unit = {
+ events.clear()
+ }
+
+ override def startDocument(): Boolean = {
+ events.append(TestInfosetEvent.startDocument())
+ true
+ }
+
+ override def endDocument(): Boolean = {
+ events.append(TestInfosetEvent.endDocument())
+ true
+ }
+
+ override def startSimple(diSimple: DISimple): Boolean = {
+ events.append(
+ TestInfosetEvent.startSimple(
+ diSimple.erd.name,
+ diSimple.erd.namedQName.namespace,
+ diSimple.dataValueAsString,
+ if (diSimple.erd.isNillable) MaybeBoolean(diSimple.isNilled) else
MaybeBoolean.Nope))
+ true
+ }
+
+ override def endSimple(diSimple: DISimple): Boolean = {
+ events.append(
+ TestInfosetEvent.endSimple(
+ diSimple.erd.name,
+ diSimple.erd.namedQName.namespace))
+ true
+ }
+
+ override def startComplex(diComplex: DIComplex): Boolean = {
+ events.append(
+ TestInfosetEvent.startComplex(
+ diComplex.erd.name,
+ diComplex.erd.namedQName.namespace,
+ if (diComplex.erd.isNillable) MaybeBoolean(diComplex.isNilled) else
MaybeBoolean.Nope))
+ true
+ }
+
+ override def endComplex(diComplex: DIComplex): Boolean = {
+ events.append(
+ TestInfosetEvent.endComplex(
+ diComplex.erd.name,
+ diComplex.erd.namedQName.namespace))
+ true
+ }
+
+ override def startArray(diArray: DIArray): Boolean = {
+ true
+ }
+
+ override def endArray(diArray: DIArray): Boolean = {
+ true
+ }
+}
diff --git
a/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala
index f243ed6..c26373c 100644
---
a/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala
+++
b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala
@@ -29,6 +29,7 @@ import javax.xml.XMLConstants
import org.apache.commons.io.FileUtils
+import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -1125,4 +1126,45 @@ class TestScalaAPI {
}
}
+
+ @Test
+ def testScalaAPI25(): Unit = {
+ // Demonstrates the use of a custom InfosetInputter/Outputter
+
+ val expectedData = "42"
+ val expectedEvents = Array(
+ TestInfosetEvent.startDocument(),
+ TestInfosetEvent.startComplex("e1", "http://example.com"),
+ TestInfosetEvent.startSimple("e2", "http://example.com", expectedData),
+ TestInfosetEvent.endSimple("e2", "http://example.com"),
+ TestInfosetEvent.endComplex("e1", "http://example.com"),
+ TestInfosetEvent.endDocument()
+ )
+
+ val c = Daffodil.compiler()
+
+ val schemaFile = getResource("/test/sapi/mySchema1.dfdl.xsd")
+ val pf = c.compileFile(schemaFile)
+ val dp = pf.onPath("/")
+
+ val file = getResource("/test/sapi/myData.dat")
+ val fis = new java.io.FileInputStream(file)
+ val dis = new InputSourceDataInputStream(fis)
+ val outputter = new TestInfosetOutputter()
+ val pr = dp.parse(dis, outputter)
+
+ assertFalse(pr.isError())
+ assertArrayEquals(
+ expectedEvents.asInstanceOf[Array[Object]],
+ outputter.events.toArray.asInstanceOf[Array[Object]]
+ )
+
+ val bos = new java.io.ByteArrayOutputStream()
+ val wbc = java.nio.channels.Channels.newChannel(bos)
+ val inputter = new TestInfosetInputter(expectedEvents: _*)
+
+ val ur = dp.unparse(inputter, wbc)
+ assertFalse(ur.isError)
+ assertEquals(expectedData, bos.toString())
+ }
}
diff --git
a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetInputter.scala
b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetInputter.scala
index aa93891..e5be69e 100644
---
a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetInputter.scala
+++
b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetInputter.scala
@@ -64,11 +64,11 @@ class TDMLInfosetInputter(val scalaInputter:
ScalaXMLInfosetInputter, others: Se
res
}
- override def getSimpleText(primType: NodeInfo.Kind): String = {
- val res = scalaInputter.getSimpleText(primType)
+ override def getSimpleText(primType: NodeInfo.Kind, runtimeProperties:
java.util.Map[String, String]): String = {
+ val res = scalaInputter.getSimpleText(primType, runtimeProperties)
val resIsEmpty = res == null || res == ""
val otherStrings = others.map { i =>
- val firstVersion = i.getSimpleText(primType)
+ val firstVersion = i.getSimpleText(primType, runtimeProperties)
val finalVersion = i match {
case _ if (firstVersion eq null) => ""
// the json infoset inputter maintains CRLF/CR, but XML converts
CRLF/CR to