stevedlawrence commented on code in PR #1176:
URL: https://github.com/apache/daffodil/pull/1176#discussion_r1517689566
##########
daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/FixedLengthLayer.scala:
##########
@@ -42,33 +40,35 @@ import org.apache.commons.io.IOUtils
* This length is enforced on both parsing and unparsing the layer.
* There are no output/result DFDL variables from this layer.
*/
-final class FixedLengthLayer(fixedLength: JLong)
+final class FixedLengthLayer
extends Layer("fixedLength", "urn:org.apache.daffodil.layers.fixedLength") {
- Assert.invariant(fixedLength > 0)
+ private var fixedLength: Long = 0
- /** Required for SPI class loading */
- def this() = this(1)
-
- private def maxFixedLength = Short.MaxValue
-
- override def wrapLayerInput(jis: InputStream, lr: LayerRuntime): InputStream
= {
+ def setLayerVariableParameters(fixedLength: Long): Unit = {
+ this.fixedLength = fixedLength
+ }
+ private def check(lr: LayerRuntime): Unit = {
+ if (fixedLength < 1)
Review Comment:
Question above about the possibiity for setLayerVariableParameters to
perform validation checks would mean this check function can go away, and wrap
functions can always assume valid state.
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/ChecksumLayerBase.scala:
##########
@@ -38,11 +38,35 @@ import org.apache.commons.io.IOUtils
abstract class ChecksumLayerBase(
localName: String,
namespace: String,
- val length: Int,
) extends Layer(localName, namespace) {
private var checksum: Int = -1
+ private var length: Int = -1
+ private var isInitialized: Boolean = false
+
+ final def getLength: Int = length
+ final protected def setLength(len: Int): Unit = {
+ this.length = len
+ }
+
+ /**
+ * Initializes the layer.
+ *
+ * This method is called to ensure that `setLength` has been called before
the streams are created.
+ * It can be overridden in subclasses, but those must run the
`super.init(lr)`
+ *
+ * @param lr The LayerRuntime instance.
+ */
+ protected def init(lr: LayerRuntime): Unit = {
Review Comment:
If setVariable can check errors I think init also goes away?
Then setLength would create failures if length is <= 0 however setVariable
would?
I guess we still need a check to make sure implementations actually call
setLength, so the wrap functions do need some kind of initialization check.
Maybe you could change the var to a JInt that is initialized to null, and the
wrap functions could do something simple like `Assert.invariant(length !=
null)`. Just trying to think of ways to makes things as simple as possible.
Also, it feels like if a user doesn't cal setLength, that wants to be an
error more aggressive than a processing error. That's a bug in the layer and
needs to be fixed.
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/Layer.java:
##########
@@ -63,11 +63,11 @@
* <p/>
* A layer that wants to return a value after the layer algorithm completes
defines a special recognizable
* getter method. The name of the getter is formed from prefixing the DFDL
variable name with the string
- * 'getDFDLResultVariable_'. The return type of the getter must match the type
of the variable.
+ * 'getLayerVariableResult_'. The return type of the getter must match the
type of the variable.
* <p/>
* For example, a result value getter for a DFDL variable named 'checksum' of
type xs:unsignedShort would be:
* <pre>
- * Int getDFDLResultVariable_checksum() {
+ * Int getLayerVariableResult_checksum() {
Review Comment:
Do these set/getVariable functions need o be public, or can reflection still
find them if they are package private?
Also, it might be worth using `int` instead of `Int` since this is javadoc.
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/LayerFactory.scala:
##########
@@ -65,73 +72,87 @@ object LayerFactory {
// find another constructor and check that there is an argument
// corresponding to each of the layer variables.
val c = protoLayer.getClass
- val allConstructors = c.getConstructors.toSeq
-
- val constructor: Constructor[_] =
- if (allConstructors.length == 1) {
- // There is only the default constructor.
- // That's ok if there are no variables for the layer, which we check
later.
- allConstructors.head
- } else if (allConstructors.length == 2) {
- allConstructors.filter(_.getParameterCount > 0).head
- } else {
- def tooManyConstructorsMsg: String = {
- s"""Layer class $c has multiple non-default constructors. It
should have a default (no args)
- | constructor and a single additional constructor with
arguments for
- | each of the layer's parameter variables.""".stripMargin
- }
-
- lrd.context.SDE(tooManyConstructorsMsg)
- }
+ val constructor = c.getConstructors.find { c => c.getParameterCount == 0
}.getOrElse {
+ Assert.invariantFailed("SPI-loaded class without default constructor?
Not possible.")
+ }
+ val allMethods = c.getMethods
+ val optParamSetter = allMethods.find { _.getName == varParamSetter }
+ val allVarResultGetters =
+ ListSet(allMethods.filter { m =>
+ val nom = m.getName
+ nom.startsWith(varResultPrefix)
+ }.toSeq: _*)
- if (lrd.vmap.isEmpty && allConstructors.length == 1) {
+ if (lrd.vmap.isEmpty && optParamSetter.isEmpty &&
allVarResultGetters.isEmpty) {
// there are no vars, we're done
- new LayerVarsRuntime(constructor, Nil, Nil)
+ new LayerVarsRuntime(constructor, None, Nil, Nil)
} else {
- // there is a constructor with args that are supposed to correspond to
bound vars
- val params = constructor.getParameters.toSeq
- val nParams = params.length
- val nVars = lrd.vmap.size
+ val allLayerVRDs = ListSet(lrd.vmap.toSeq.map(_._2): _*)
+ // there is either a params setter, a result getter, or both.
+ val paramVRDs: Seq[VariableRuntimeData] =
+ optParamSetter.toSeq.flatMap { paramSetter =>
+ // there is a param setter with args that are supposed to
correspond to bound vars
+ val params = paramSetter.getParameters.toSeq
- val paramTypes = constructor.getParameterTypes.toSeq
- val paramVRDs = params.map { p =>
- lrd.vmap.getOrElse(
- p.getName,
- lrd.context.SDE(
- s"No layer DFDL variable named '$p.getName' was found in
namespace ${lrd.namespace}.",
- ),
- )
- }.toSeq
+ val paramTypes = paramSetter.getParameterTypes.toSeq
+ val pVRDs: Seq[VariableRuntimeData] = params.map { p =>
+ lrd.vmap.getOrElse(
+ p.getName,
+ lrd.context.SDE(
+ s"No layer DFDL variable named '$p.getName' was found in
namespace ${lrd.namespace}.",
+ ),
+ )
+ }.toSeq
- // Now we deal with the result getters and the corresponding vars
- //
- val allLayerVRDs = ListSet(lrd.vmap.toSeq.map(_._2): _*)
- //
- // verify only Int and String are allowed simple types for now.
- // The rest of the code beyond here doesn't care what the type is, but
we don't want to
- // create a bunch of test code for layer things that aren't needed.
- //
- allLayerVRDs.foreach { vrd =>
- lrd.context.schemaDefinitionUnless(
- vrd.primType == PrimType.String ||
- vrd.primType == PrimType.Int ||
- vrd.primType == PrimType.UnsignedShort,
- s"Layer variable ${vrd.globalQName.toQNameString} of type
${vrd.primType.dfdlType} must be of type xs:int, xs:unsignedShort, or
xs:string.",
- )
- }
+ // Now we deal with the result getters and the corresponding vars
+ //
+
+ //
+ // verify only Int and String are allowed simple types for now.
+ // The rest of the code beyond here doesn't care what the type is,
but we don't want to
+ // create a bunch of test code for layer things that aren't needed.
Review Comment:
As far as test code, we could have a single layer that has one variable for
each type we want to support. Then testing each type just requires adding a new
dfdl:defineVariable and a new parameter to setVariable. Doing that now seems
worth it to avoid someone coming a long and saying "I really need unsignedLong"
or something, especially considering the rest of the code doesn't care. Or at
least if we add that test now, we have a place to test to extend when we do
want to add support for new types.
##########
daffodil-test/src/test/scala/org/apache/daffodil/runtime1/layers/IPv4ChecksumLayer.scala:
##########
@@ -36,38 +36,42 @@ final class IPv4ChecksumLayer()
extends ChecksumLayer(
"IPv4Checksum",
"urn:org.apache.daffodil.layers.IPv4Checksum",
- 20,
) {
- /**
- * Result DFDL variable value getter. This getter is called to obtain the
value for,
- * and populate the DFDL variable named checkDigit, in the layer's target
namespace.
- * @return the check digit value
- */
- def getDFDLResultVariable_IPv4Checksum: JInt =
- getChecksum
-
/**
* This layer is always exactly 20 bytes long.
*/
private def lenInBytes = 20
private def chksumShortIndex = 5
+ override protected def init(lr: LayerRuntime): Unit = {
+ setLength(lenInBytes)
+ super.init(lr)
+ }
+
+ /**
+ * Result DFDL variable value getter. This getter is called to obtain the
value for,
+ * and populate the DFDL variable named checkDigit, in the layer's target
namespace.
+ * @return the check digit value
+ */
+ def getLayerVariableResult_IPv4Checksum: JInt = getChecksum
+
/**
* Computes the checksum value.
* When unparsing this also modifies the output bytes to have the checksum
at the
* middle location as per the IPv4 spec.
* The LayerChecksumMixin assigns the computed checksum value to the first
variable.
- * @param layerCompileInfo structure providing access to state such as
variables, when needed.
+ * @param layerRuntime structure providing access to state such as
variables, when needed.
* @param isUnparse true if the checksum is for unparsing.
* @param byteBuffer contains the 20 bytes to be used to compute this
checksum.
* @return the computed checksum value as a 32-bit signed Int.
*/
override def compute(
- layerCompileInfo: LayerRuntime,
+ layerRuntime: LayerRuntime,
isUnparse: Boolean,
byteBuffer: ByteBuffer,
): Int = {
+ init(layerRuntime)
Review Comment:
I'm not a huge fan of this init, it feels an awful lot like the check()
function that we tried to remove. Maybe it's required in this case? I did
mention a possible alternative in another comment where the set variable
function calls setLength, but I guess the issue here is this layer don't have
the a set variable function to call setLength since this layer has no variables?
A thought, does it work to have an empty set variable functions? For
example, maybe this checksum layer implementation could do:
```scala
def setLayerVariableParameters(): Unit = {
setLength(20)
}
```
So the documentation for ChecksumLayer api says all implementations must
implement a setLayerVariableParameters function and must call setLength(). If
the layer has no variables, the function can have an empty parameter list.
So setLayerVariablParameters kindof becomes a layers `init()` function, and
it can be parameterized by variables or not?
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/LayerFactory.scala:
##########
@@ -65,73 +72,87 @@ object LayerFactory {
// find another constructor and check that there is an argument
// corresponding to each of the layer variables.
val c = protoLayer.getClass
- val allConstructors = c.getConstructors.toSeq
-
- val constructor: Constructor[_] =
- if (allConstructors.length == 1) {
- // There is only the default constructor.
- // That's ok if there are no variables for the layer, which we check
later.
- allConstructors.head
- } else if (allConstructors.length == 2) {
- allConstructors.filter(_.getParameterCount > 0).head
- } else {
- def tooManyConstructorsMsg: String = {
- s"""Layer class $c has multiple non-default constructors. It
should have a default (no args)
- | constructor and a single additional constructor with
arguments for
- | each of the layer's parameter variables.""".stripMargin
- }
-
- lrd.context.SDE(tooManyConstructorsMsg)
- }
+ val constructor = c.getConstructors.find { c => c.getParameterCount == 0
}.getOrElse {
+ Assert.invariantFailed("SPI-loaded class without default constructor?
Not possible.")
+ }
+ val allMethods = c.getMethods
+ val optParamSetter = allMethods.find { _.getName == varParamSetter }
Review Comment:
Should we check if there there are multiple overloaded varParamSetter an
error?
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/ChecksumLayer.java:
##########
@@ -23,29 +23,29 @@
/**
* A checksum layer computes some sort of integer value from a region of the
data stream. The term checksum is
* used generically here to subsume all sorts of CRCs, check digits, data
hash, and digest calculations.
- *
+ * <p/>
* This base is suitable only for checksums computed over small sections of
data, not large data streams or whole files.
- *
+ * <p/>
* The entire region of data the checksum is being computed over, will be
pulled into a byte buffer in memory.
- *
+ * <p/>
* The resulting checksum is the return value of the compute method.
- *
+ * <p/>
* This is delivered into a DFDL variable for use by the DFDL schema. This
variable can have any name
* such as 'crc', 'digest', or 'dataHash'.
- *
+ * <p/>
* The derived implementation class must also define a getter method based on
the name of the DFDL variable which
* will be assigned with the checksum value. For example if the checksum is
actually a specific digest/hash calculation
* and the DFDL variable is named 'digest', then this getter must be defined:
- *
- * Int getDFDLResultVariable_digest() { return getChecksum() }
- *
+ * <p/>
+ * Int getLayerVariableResult_digest() { return getChecksum() }
+ * <p/>
* This will be called automatically to retrieve the integer value that was
returned from the `compute` method, and
* the DFDL variable 'digest' will be assigned that value.
*/
public abstract class ChecksumLayer extends ChecksumLayerBase {
Review Comment:
Do we need a comment that implementations must call `setLength` prior to
wrap being called?
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/LayerFactory.scala:
##########
@@ -65,73 +72,87 @@ object LayerFactory {
// find another constructor and check that there is an argument
// corresponding to each of the layer variables.
val c = protoLayer.getClass
- val allConstructors = c.getConstructors.toSeq
-
- val constructor: Constructor[_] =
- if (allConstructors.length == 1) {
- // There is only the default constructor.
- // That's ok if there are no variables for the layer, which we check
later.
- allConstructors.head
- } else if (allConstructors.length == 2) {
- allConstructors.filter(_.getParameterCount > 0).head
- } else {
- def tooManyConstructorsMsg: String = {
- s"""Layer class $c has multiple non-default constructors. It
should have a default (no args)
- | constructor and a single additional constructor with
arguments for
- | each of the layer's parameter variables.""".stripMargin
- }
-
- lrd.context.SDE(tooManyConstructorsMsg)
- }
+ val constructor = c.getConstructors.find { c => c.getParameterCount == 0
}.getOrElse {
Review Comment:
Can this be simplified to this to get the zero arg constructor:
```scala
val constructor = c.getConstructor()
```
In order for SPI to have found the layer and gotten this far, and created an
instance this must return something, right?
##########
daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/BoundaryMarkLayer.scala:
##########
@@ -45,21 +45,29 @@ import org.apache.daffodil.runtime1.layers.api.LayerRuntime
* disrupt the search for the boundary mark string.
*
* This layer does not populate any DFDL variables with results.
+ *
* This layer defines two DFDL variables which provide required parameters.
- * @param boundaryMark a string which is the boundary marker. Searching for
this string is
- * done without any notion of escaping. When parsing the
data in the
- * layer is up to but not including that of the boundary
mark string,
- * which is removed from the data-stream on parsing, and
inserted into
- * the data stream on unparsing.
- * @param layerEncoding a string which is the name of the character set
encoding used to
- * decode characters during the search for the boundary
mark, and used
- * to encode characters when inserting the boundary mark
when unparsing.
*/
-final class BoundaryMarkLayer(boundaryMark: String, layerEncoding: String)
+final class BoundaryMarkLayer
extends Layer("boundaryMark", "urn:org.apache.daffodil.layers.boundaryMark")
{
- /** no arg contructor required for SPI class loading */
- def this() = this("dummy", "ascii")
+ private var boundaryMark: String = _
+ private var layerEncoding: String = _
+
+ /**
+ * @param boundaryMark a string which is the boundary marker. Searching for
this string is
+ * done without any notion of escaping. When parsing
the data in the
+ * layer is up to but not including that of the
boundary mark string,
+ * which is removed from the data-stream on parsing,
and inserted into
+ * the data stream on unparsing.
+ * @param layerEncoding a string which is the name of the character set
encoding used to
+ * decode characters during the search for the boundary
mark, and used
+ * to encode characters when inserting the boundary
mark when unparsing.
+ */
+ def setLayerVariableParameters(boundaryMark: String, layerEncoding: String):
Unit = {
+ this.boundaryMark = boundaryMark
+ this.layerEncoding = layerEncoding
Review Comment:
Should we pass in LayerRuntime into this function for so users can throw
errors here? For example, this might want to be something like:
```scala
private var boundaryMark: String = _
private var charset: Charset = _
def setLayerVariableParameters(lr: LayerRuntime, boundryMark: String,
layerEncoding: String): Unit = {
this.boundaryMark = boundaryMake
this.charset = try {
Charset.forName(layerEncoding)
} catch {
case e: Exception => lr.runtimeSchemaDefinitionError(...)
}
}
```
Or alternatively, maybe `setLayerVariableParameters` can throw an exception
(maybe IllegalArgumentException or a custom LayerBadVariableException?) if any
variables values don't make sense for a layer? And the layer backend swould
catch that and convert it to an SDE?
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/Layer.java:
##########
@@ -63,11 +63,11 @@
* <p/>
Review Comment:
THis doc needs updating to use the new setVariable method.
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/LayerFactory.scala:
##########
@@ -0,0 +1,326 @@
+/*
+ * 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.runtime1.layers
+
+import java.lang.reflect.Constructor
+import java.lang.reflect.Method
+import scala.collection.immutable.ListSet
+import scala.collection.mutable
+
+import org.apache.daffodil.lib.exceptions.Assert
+import org.apache.daffodil.runtime1.dpath.NodeInfo.PrimType
+import org.apache.daffodil.runtime1.infoset.DataValue
+import org.apache.daffodil.runtime1.layers.api.Layer
+import org.apache.daffodil.runtime1.layers.api.LayerException
+import org.apache.daffodil.runtime1.processors.VariableRuntimeData
+
+object LayerFactory {
+
+ /** cache that maps spiName of layer to the LayerVarsRuntime */
+ private lazy val alreadyCheckedLayers =
+ new mutable.LinkedHashMap[String, LayerVarsRuntime]()
+
+ /**
+ * Computes the things that can be computed once-only to make calling the
constructor
+ * and passing the parameter variables faster at runtime. This also does all
the
+ * checking that the constructor has an argument for each variable of
properly matching type, and
+ * has a getter for each result variable, again returning the proper type.
+ *
+ * This is called at schema compile time to ensure the layer code is
properly defined and matches
+ * the layer variable definitions.
+ *
+ * It is called again at runtime after the Layer class is loaded by the SPI
+ * to ensure that the loaded layer class constructor signature at least
matches the layer
+ * variables defined in the schema.
+ * @param lrd
+ * @param protoLayer the layer instance allocated by the SPI loader
(zero-arg constructed)
+ * @return
+ */
+ def computeLayerVarsRuntime(lrd: LayerRuntimeData, protoLayer: Layer):
LayerVarsRuntime = {
+ val optLayerVarsRuntime = alreadyCheckedLayers.get(protoLayer.name())
+ optLayerVarsRuntime.getOrElse {
+ // we know there is a default zero arg constructor
+ // find another constructor and check that there is an argument
+ // corresponding to each of the layer variables.
+ val c = protoLayer.getClass
+ val allConstructors = c.getConstructors.toSeq
+
+ val constructor: Constructor[_] =
+ if (allConstructors.length == 1) {
+ // There is only the default constructor.
+ // That's ok if there are no variables for the layer, which we check
later.
+ allConstructors.head
+ } else if (allConstructors.length == 2) {
+ allConstructors.filter(_.getParameterCount > 0).head
+ } else {
+ def tooManyConstructorsMsg: String = {
+ s"""Layer class $c has multiple non-default constructors. It
should have a default (no args)
+ | constructor and a single additional constructor with
arguments for
+ | each of the layer's parameter variables.""".stripMargin
+ }
+ lrd.context.SDE(tooManyConstructorsMsg)
+ }
+
+ if (lrd.vmap.isEmpty && allConstructors.length == 1) {
+ // there are no vars, we're done
+ new LayerVarsRuntime(constructor, Nil, Nil)
+ } else {
+ // there is a constructor with args that are supposed to correspond to
bound vars
+ val params = constructor.getParameters.toSeq
+ val nParams = params.length
+ val nVars = lrd.vmap.size
+
+ val paramTypes = constructor.getParameterTypes.toSeq
+ val paramVRDs = params.map { p =>
+ lrd.vmap.getOrElse(
+ p.getName,
+ lrd.context.SDE(s"No layer DFDL variable named '$p.getName' was
found."),
+ )
+ }.toSeq
+
+ // Now we deal with the result getters and the corresponding vars
+ //
+ val allLayerVRDs = ListSet(lrd.vmap.toSeq.map(_._2): _*)
+ val returnVRDs = allLayerVRDs -- paramVRDs // set subtraction
Review Comment:
This was resolved without comment, just wanted to make sure you saw it.
I don't feel strongly about it, but it might simplify the logic if we didn't
require that all variables had to be either be parameters or results. For
example, getting the getting paris could be something like
```scala
val resultVarParis = allVarResultGetters.map { method =>
val varName = method.getName.drop(varResultPrefix.length)
val vrd = vmap.find { QName(layer.getNamespace, varName)
}.getOrElse(SDE(...))
val vrdClass = PrimType.toJavaType(vrd.primType.dfdlType)
schemaDefinitionUnless(compatibleTypes(vrdClass, method.getReturnType),
...)
(valVRD, method)
}
```
I'm sure that's a bit of an over simplification, but it it feels like it's
less complex? Note that LayerRuntimeData doesn't even need the vmap anymore, we
can just directly look up variables in the normal vmap, so that goes away too.
##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/LayerFactory.scala:
##########
@@ -65,73 +72,87 @@ object LayerFactory {
// find another constructor and check that there is an argument
// corresponding to each of the layer variables.
val c = protoLayer.getClass
- val allConstructors = c.getConstructors.toSeq
-
- val constructor: Constructor[_] =
- if (allConstructors.length == 1) {
- // There is only the default constructor.
- // That's ok if there are no variables for the layer, which we check
later.
- allConstructors.head
- } else if (allConstructors.length == 2) {
- allConstructors.filter(_.getParameterCount > 0).head
- } else {
- def tooManyConstructorsMsg: String = {
- s"""Layer class $c has multiple non-default constructors. It
should have a default (no args)
- | constructor and a single additional constructor with
arguments for
- | each of the layer's parameter variables.""".stripMargin
- }
-
- lrd.context.SDE(tooManyConstructorsMsg)
- }
+ val constructor = c.getConstructors.find { c => c.getParameterCount == 0
}.getOrElse {
+ Assert.invariantFailed("SPI-loaded class without default constructor?
Not possible.")
+ }
+ val allMethods = c.getMethods
+ val optParamSetter = allMethods.find { _.getName == varParamSetter }
+ val allVarResultGetters =
+ ListSet(allMethods.filter { m =>
+ val nom = m.getName
+ nom.startsWith(varResultPrefix)
+ }.toSeq: _*)
- if (lrd.vmap.isEmpty && allConstructors.length == 1) {
+ if (lrd.vmap.isEmpty && optParamSetter.isEmpty &&
allVarResultGetters.isEmpty) {
// there are no vars, we're done
- new LayerVarsRuntime(constructor, Nil, Nil)
+ new LayerVarsRuntime(constructor, None, Nil, Nil)
} else {
- // there is a constructor with args that are supposed to correspond to
bound vars
- val params = constructor.getParameters.toSeq
- val nParams = params.length
- val nVars = lrd.vmap.size
+ val allLayerVRDs = ListSet(lrd.vmap.toSeq.map(_._2): _*)
+ // there is either a params setter, a result getter, or both.
+ val paramVRDs: Seq[VariableRuntimeData] =
+ optParamSetter.toSeq.flatMap { paramSetter =>
+ // there is a param setter with args that are supposed to
correspond to bound vars
+ val params = paramSetter.getParameters.toSeq
- val paramTypes = constructor.getParameterTypes.toSeq
- val paramVRDs = params.map { p =>
- lrd.vmap.getOrElse(
- p.getName,
- lrd.context.SDE(
- s"No layer DFDL variable named '$p.getName' was found in
namespace ${lrd.namespace}.",
- ),
- )
- }.toSeq
+ val paramTypes = paramSetter.getParameterTypes.toSeq
+ val pVRDs: Seq[VariableRuntimeData] = params.map { p =>
+ lrd.vmap.getOrElse(
+ p.getName,
+ lrd.context.SDE(
+ s"No layer DFDL variable named '$p.getName' was found in
namespace ${lrd.namespace}.",
+ ),
+ )
+ }.toSeq
- // Now we deal with the result getters and the corresponding vars
- //
- val allLayerVRDs = ListSet(lrd.vmap.toSeq.map(_._2): _*)
- //
- // verify only Int and String are allowed simple types for now.
- // The rest of the code beyond here doesn't care what the type is, but
we don't want to
- // create a bunch of test code for layer things that aren't needed.
- //
- allLayerVRDs.foreach { vrd =>
- lrd.context.schemaDefinitionUnless(
- vrd.primType == PrimType.String ||
- vrd.primType == PrimType.Int ||
- vrd.primType == PrimType.UnsignedShort,
- s"Layer variable ${vrd.globalQName.toQNameString} of type
${vrd.primType.dfdlType} must be of type xs:int, xs:unsignedShort, or
xs:string.",
- )
- }
+ // Now we deal with the result getters and the corresponding vars
+ //
+
+ //
+ // verify only Int and String are allowed simple types for now.
+ // The rest of the code beyond here doesn't care what the type is,
but we don't want to
+ // create a bunch of test code for layer things that aren't needed.
+ //
+ allLayerVRDs.foreach { vrd =>
+ lrd.context.schemaDefinitionUnless(
+ vrd.primType == PrimType.String ||
+ vrd.primType == PrimType.Int ||
+ vrd.primType == PrimType.UnsignedShort ||
+ vrd.primType == PrimType.UnsignedInt,
+ s"Layer variable ${vrd.globalQName.toQNameString} of type
${vrd.primType.dfdlType} must" +
+ s" be of type xs:int, xs:unsignedInt, xs:unsignedShort, or
xs:string.",
+ )
+ }
+ // at this point the number of vars and number of setter args match
+
+ val typePairs = (pVRDs.zip(paramTypes)).toSeq
+ typePairs.foreach { case (vrd, pt) =>
+ val vrdClass = PrimType.toJavaType(vrd.primType.dfdlType)
+ lrd.context.schemaDefinitionUnless(
+ compatibleTypes(vrdClass, pt),
+ s"""Layer setter argument ${vrd.globalQName.local} and the
corresponding
+ |Layer DFDL variable have differing types: ${pt.getName}
+ | and ${vrdClass.getName} respectively.""".stripMargin,
+ )
+ }
+ if (pVRDs.length != params.length) {
Review Comment:
I think this check will never fail and can be removed? Above you have `val
pVRDs = params.map { ... }` so the lengths will always be the same.
##########
daffodil-test/src/test/scala/org/apache/daffodil/runtime1/layers/CheckDigitLayer.scala:
##########
@@ -47,33 +47,37 @@ object CheckDigitLayer {
* - digitEncoding is a string which is the name of a character set encoding
which
* defines the character set of the digits.
* This must match the dfdl:encoding for the DFDL element declaration using
this layer.
- *
- * @param length the value of the length DFDL variable
- * @param params the value of the params DFDL variable
- * @param digitEncoding the value of the digitEncoding DFDL variable
*/
-class CheckDigitLayer(length: JInt, params: String, digitEncoding: String)
+class CheckDigitLayer
extends ChecksumLayer(
"checkDigit", // name of the layer, also happens to be the name of the
output variable.
CheckDigitLayer.ns,
- length,
) {
+ private var params: String = _
+ private var digitEncoding: String = _
+ private lazy val charset = Charset.forName(digitEncoding)
+ private lazy val isVerbose = params.toLowerCase.contains("verbose")
+
+ /**
+ * @param length the value of the length DFDL variable
+ * @param params the value of the params DFDL variable
+ * @param digitEncoding the value of the digitEncoding DFDL variable
+ */
+ def setLayerVariableParameters(length: Int, params: String, digitEncoding:
String): Unit = {
+ setLength(length)
+ this.params = params
+ this.digitEncoding = digitEncoding
+ }
+
/**
* Result DFDL variable value getter. This getter is called to obtain the
value for,
* and populate the DFDL variable named checkDigit, in the layer's target
namespace.
* @return the check digit value
*/
- def getDFDLResultVariable_checkDigit: JInt =
+ def getLayerVariableResult_checkDigit: JInt =
Review Comment:
Just curious if this must be a Jint or would a primitive Int work too? I
think it would work with the new changes? Should our examples use primitive int
since I imagine that what most Java devs would find more natural?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]