private def computeLayerVarsRuntime()

in daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/LayerRuntimeCompiler.scala [67:193]


  private def computeLayerVarsRuntime(
    lrd: LayerRuntimeData,
    protoLayer: Layer
  ): LayerVarsRuntime = {
    val optLayerVarsRuntime = alreadyCheckedLayers.get(protoLayer.name())
    optLayerVarsRuntime.getOrElse {
      val c = protoLayer.getClass
      val constructor = c.getConstructor() // cannot fail because SPI loader would have failed
      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.qNameToVRD.isEmpty && optParamSetter.isEmpty && allVarResultGetters.isEmpty) {
        // there are no vars, so no setter, and no getter(s). We're done
        new LayerVarsRuntime(constructor, None, Nil, Nil)
      } else {
        val allLayerVRDs = ListSet(lrd.qNameToVRD.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
            // it is allowed for it to have zero args. Then it's purpose is just initialization, but it
            // must still get called.
            val params = paramSetter.getParameters.toSeq
            val paramTypes = paramSetter.getParameterTypes.toSeq
            val pVRDs: Seq[VariableRuntimeData] = (params.zip(paramTypes)).map { case (p, pt) =>
              val vrd = lrd.qNameToVRD.getOrElse(
                p.getName,
                lrd.context.SDE(
                  s"No layer DFDL variable named '${p.getName}' was found in namespace ${lrd.namespace}."
                )
              )
              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
              )
              vrd
            }

            // Now we deal with the result getters and the corresponding vars
            //
            pVRDs
          }
        val nParams = paramVRDs.length
        val nVars = lrd.qNameToVRD.size
        val returnVRDs = allLayerVRDs -- paramVRDs // set subtraction

        // each returnVRD needs to have a corresponding getter method
        val returnVRDNames = returnVRDs.map(_.globalQName.local)
        val resultGettersNames = allVarResultGetters.map(_.getName.replace(varResultPrefix, ""))
        val nResultGetters = resultGettersNames.size

        val javaParamSetterArgs =
          allLayerVRDs
            .map { vrd =>
              s"type: ${PrimType.toJavaTypeString(vrd.primType.dfdlType)} name: ${vrd.globalQName.local}"
            }
            .mkString(", ")

        lrd.context.schemaDefinitionUnless(
          nParams + nResultGetters == nVars,
          s"""Layer class $c does not have a setter with arguments for each of the layer's variables.
             | It should have a setter named $varParamSetter with an argument for each layer parameter, in any order, such as
             | ($javaParamSetterArgs), and a getter for remaining layer variables, named with a specific
             |  name prefix like: ' $varResultPrefix '.""".stripMargin
        )

        val returnVRDsWithoutGetters = returnVRDNames -- resultGettersNames
        val resultGettersWithoutVRDs = resultGettersNames -- returnVRDNames
        lrd.context.schemaDefinitionUnless(
          returnVRDsWithoutGetters.isEmpty,
          s"""The layer variables ${returnVRDsWithoutGetters.mkString(
              ","
            )} have no corresponding getters."""
        )
        lrd.context.schemaDefinitionUnless(
          resultGettersWithoutVRDs.isEmpty, {
            val getterFullNames = returnVRDsWithoutGetters.map { vname =>
              this.varResultPrefix + vname
            }
            s"""The getters ${getterFullNames.mkString(
                ","
              )} have no corresponding layer variables."""
          }
        )
        // at this point we know each variable that was not a parameter of the setter
        // has a getter with matching name.
        val resultVarPairs = resultGettersNames.map { (rgn: String) =>
          val getter: Method =
            allVarResultGetters
              .find { (g: Method) => g.getName == varResultPrefix + rgn }
              .getOrElse {
                Assert.invariantFailed("no getter for getter name.")
              }
          val vrd = returnVRDs.find { vrd => vrd.globalQName.local == rgn }.getOrElse {
            Assert.invariantFailed("no vrd for getter name.")
          }
          (vrd, getter)
        }
        resultVarPairs.foreach { case (vrd, getter) =>
          val vrdClass = PrimType.toJavaType(vrd.primType.dfdlType)
          val gt = getter.getReturnType
          lrd.context.schemaDefinitionUnless(
            compatibleTypes(vrdClass, gt),
            s"""Layer return variable ${vrd.globalQName.local} and the corresponding
               |Layer getter have differing types: ${vrdClass.getName}
               | and ${gt.getName} respectively.""".stripMargin
          )
          lrd.context.schemaDefinitionUnless(
            getter.getParameterCount == 0,
            s"""Layer return variable getter ${getter.getName} must have no arguments."""
          )
        }
        val lrv =
          new LayerVarsRuntime(constructor, optParamSetter, paramVRDs, resultVarPairs.toSeq)
        alreadyCheckedLayers.put(lrd.spiName, lrv)
        lrv
      }
    }
  }