stevedlawrence commented on code in PR #253:
URL: https://github.com/apache/daffodil-vscode/pull/253#discussion_r963831051


##########
.vscode/launch.json:
##########
@@ -48,6 +48,15 @@
                        ],
                        "internalConsoleOptions": "openOnSessionStart",
                        "preLaunchTask": "npm: compile"
+               },
+               {
+                       "type": "scala",
+                       "request": "launch",
+                       "name": "DAPodil",

Review Comment:
   What does this actually launch? What is DAPodil?



##########
bindings.xjb:
##########
@@ -0,0 +1,58 @@
+<jxb:bindings version="2.1"

Review Comment:
   This needs a license header.
   
   Also, is this generated? Can this just be created as part of the build 
process?



##########
bindings.xjb:
##########
@@ -0,0 +1,58 @@
+<jxb:bindings version="2.1"
+          xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData";
+          xmlns:jxb="http://java.sun.com/xml/ns/jaxb";
+          xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/";
+          xmlns:xs="http://www.w3.org/2001/XMLSchema";>
+    <jxb:globalBindings>
+        <jxb:serializable uid="1" />
+    </jxb:globalBindings>
+    <jxb:bindings schemaLocation="resources/xsd/DFDL_part3_model.xsd" 
version="1.0">
+        <jxb:bindings 
node="//xs:attributeGroup[@name='SetVariableAG']/xs:attribute[@name='value']">
+            <jxb:property name="ValueAttribute"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:element[@name='simpleType']">
+            <jxb:class name="simpleTypeClass"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:element[@name='group']">
+            <jxb:class name="groupClass"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:element[@name='choice']">
+            <jxb:class name="choiceClass"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:element[@name='sequence']">
+            <jxb:class name="sequenceClass"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:element[@name='element']">
+            <jxb:class name="elementClass"/>
+        </jxb:bindings>
+    </jxb:bindings>
+    <jxb:bindings schemaLocation="resources/xsd/DFDL_part2_attributes.xsd" 
version="1.0">
+        <jxb:bindings 
node="//xs:attributeGroup[@name='BaseAGQualified']/xs:attribute[@name='ref']">
+            <jxb:property name="RefAttribute"/>
+        </jxb:bindings>
+    </jxb:bindings>
+    <jxb:bindings schemaLocation="resources/xsd/DFDL_part1_simpletypes.xsd" 
version="1.0">
+        <jxb:bindings node="//xs:simpleType[@name='ByteOrderEnum']">
+            <jxb:typesafeEnumClass name="ByteOrderEnumType"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:simpleType[@name='BitOrderEnum']">
+            <jxb:typesafeEnumClass name="BitOrderEnumType"/>
+        </jxb:bindings>
+    </jxb:bindings>
+    <jxb:bindings schemaLocation="resources/xsd/dafext.xsd" version="1.0">
+        <jxb:bindings node="//xs:attribute[@name='parseUnparsePolicy']">
+            <jxb:property name="ParseUnparsePolicyExt"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:simpleType[@name='PropertyNameType']">
+            <jxb:typesafeEnumClass name="PropertyNameTypeExt"/>
+        </jxb:bindings>
+        <jxb:bindings node="//xs:complexType[@name='PropertyType']">
+            <jxb:class name="PropertyTypeClass"/>
+        </jxb:bindings>
+    </jxb:bindings>
+    <jxb:bindings schemaLocation="resources/xsd/dfdlx.xsd" version="1.0">
+        <jxb:bindings node="//xs:simpleType[@name='PropertyNameType']">
+            <jxb:typesafeEnumClass name="PropertyNameTypeX"/>
+        </jxb:bindings>
+    </jxb:bindings>
+</jxb:bindings>

Review Comment:
   What is the decision point for what bindings get included? There seems to be 
very small subset of everything defined in the schemas?



##########
build.sbt:
##########
@@ -88,3 +89,180 @@ lazy val core = project
     packageName := s"${name.value}-$daffodilVer"
   )
   .enablePlugins(commonPlugins: _*)
+  .dependsOn(sbtXjcProject)
+  .aggregate(sbtXjcProject)
+
+lazy val sbtXjcProject = project
+  .in(file("server/sbtXjc"))
+  .enablePlugins(SbtXjcPlugin)
+  .settings(
+    name := "daffodil-xjc",
+    libraryDependencies ++= Seq(
+      "javax.activation" % "activation" % "1.1.1",
+      "com.sun.xml.bind" % "jaxb-xjc" % "2.1.6"
+    ),
+    xjcCommandLine += "-nv",
+    xjcCommandLine += "-p",
+    xjcCommandLine += "org.apache.daffodil.tdml",
+    xjcBindings += "bindings.xjb",
+    xjcJvmOpts += "-classpath",
+    xjcJvmOpts += Seq(
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "javax",
+        "activation",
+        "activation",
+        "1.1.1",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "javax",
+        "activation",
+        "javax.activation-api",
+        "1.2.0",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "txw2",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "relaxngDatatype",
+        "relaxngDatatype",
+        "20020414",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "xsom",
+        "xsom",
+        "20140925",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "xml",
+        "bind",
+        "external",
+        "rngom",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "istack",
+        "istack-commons-runtime",
+        "2.21",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "codemodel",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "jaxb-core",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "javax",
+        "xml",
+        "bind",
+        "jaxb-api",
+        "2.1",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "istack",
+        "istack-commons-tools",
+        "2.21",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "jaxb-xjc",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "xml",
+        "bind",
+        "jaxb-impl",
+        "2.2.11",
+        "*"

Review Comment:
   Is adding all these paths manually really the expected usage of this? This 
seems incredibly fragile and hard to maintain, especially with the verisions in 
the. Isn't any version change going to break this? Does the sbt xjc plugin not 
handle all of this?



##########
project/Rat.scala:
##########
@@ -34,7 +34,9 @@ object Rat {
     file("images/daffodil.ico"),
     // yarn and rpm generated files
     file("yarn.lock"),
-    file("package-lock.json")
+    file("package-lock.json"),
+    // JAXB Bindings files
+    file("bindings.xjb")

Review Comment:
   If a file can have a license, it should have one. An xjb file is just XML 
and so can have a license. This should be removed from the Rat excludes.



##########
build.sbt:
##########
@@ -88,3 +89,180 @@ lazy val core = project
     packageName := s"${name.value}-$daffodilVer"
   )
   .enablePlugins(commonPlugins: _*)
+  .dependsOn(sbtXjcProject)
+  .aggregate(sbtXjcProject)
+
+lazy val sbtXjcProject = project
+  .in(file("server/sbtXjc"))
+  .enablePlugins(SbtXjcPlugin)
+  .settings(
+    name := "daffodil-xjc",
+    libraryDependencies ++= Seq(
+      "javax.activation" % "activation" % "1.1.1",
+      "com.sun.xml.bind" % "jaxb-xjc" % "2.1.6"
+    ),
+    xjcCommandLine += "-nv",
+    xjcCommandLine += "-p",
+    xjcCommandLine += "org.apache.daffodil.tdml",
+    xjcBindings += "bindings.xjb",
+    xjcJvmOpts += "-classpath",
+    xjcJvmOpts += Seq(
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "javax",
+        "activation",
+        "activation",
+        "1.1.1",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "javax",
+        "activation",
+        "javax.activation-api",
+        "1.2.0",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "txw2",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "relaxngDatatype",
+        "relaxngDatatype",
+        "20020414",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "xsom",
+        "xsom",
+        "20140925",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "xml",
+        "bind",
+        "external",
+        "rngom",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "istack",
+        "istack-commons-runtime",
+        "2.21",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "codemodel",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "jaxb-core",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "javax",
+        "xml",
+        "bind",
+        "jaxb-api",
+        "2.1",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "istack",
+        "istack-commons-tools",
+        "2.21",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "org",
+        "glassfish",
+        "jaxb",
+        "jaxb-xjc",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator),
+      Seq(
+        s"${csrCacheDirectory.value}",
+        "https",
+        "repo1.maven.org",
+        "maven2",
+        "com",
+        "sun",
+        "xml",
+        "bind",
+        "jaxb-impl",
+        "2.2.11",
+        "*"
+      ).mkString(java.io.File.separator)
+    ).mkString(java.io.File.pathSeparator),
+    Compile / xjc / sources := Seq(file("resources/xsd")),

Review Comment:
   Should these files be in the `server/sbtXjc` subproject? Though, maybe I'm 
not sure what the sbtXjc subproject is for? That directory doesn't actually 
exist?



##########
resources/xsd/xml.xsd:
##########
@@ -0,0 +1,155 @@
+<?xml version='1.0'?>
+<!--
+  Copyright © 2018 World Wide Web Consortium, (Massachusetts
+  Institute of Technology, European Research Consortium for Informatics and
+  Mathematics, Keio University, Beihang). All Rights Reserved. This work is
+  distributed under the W3C® Software License [1] in the hope that it will be
+  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+  [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
+-->

Review Comment:
   Many of these files you've copied have different licenses. If they are going 
to be copied like this (which I hope there is an alternative), then you need to 
update the LICENSE/NOTICE files accordingly.



##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala:
##########
@@ -176,71 +180,333 @@ object Parse {
         case class File(path: Path) extends InfosetOutput
       }
 
+      sealed trait TDMLConfig extends LaunchArgs
+      object TDMLConfig {
+        case class Generate(
+            schemaPath: Path,
+            dataPath: Path,
+            stopOnEntry: Boolean,
+            infosetOutput: LaunchArgs.InfosetOutput,
+            name: String,
+            description: String,
+            path: String
+        ) extends TDMLConfig
+
+        case class Append(
+            schemaPath: Path,
+            dataPath: Path,
+            stopOnEntry: Boolean,
+            infosetOutput: LaunchArgs.InfosetOutput,
+            name: String,
+            description: String,
+            path: String
+        ) extends TDMLConfig
+
+        case class Execute(
+            stopOnEntry: Boolean,
+            infosetOutput: LaunchArgs.InfosetOutput,
+            name: String,
+            description: String,
+            path: String
+        ) extends TDMLConfig
+      }
+
       def parse(arguments: JsonObject): EitherNel[String, LaunchArgs] =
+        // Determine, based on the presence of the tdmlConfig object in the 
launch config, whether
+        //   this is a "normal" DFDL operation or if we should attempt to 
parse the values from
+        //   the tdmlConfig object.
+        Option(arguments.getAsJsonObject("tdmlConfig")) match {
+          case None             => parseManual(arguments)
+          case Some(tdmlConfig) => parseTDML(arguments, tdmlConfig)
+        }
+
+      // Parse a launch config that has been found to not have a tdmlConfig 
object
+      def parseManual(arguments: JsonObject): EitherNel[String, LaunchArgs] =
         (
-          Option(arguments.getAsJsonPrimitive("program"))
-            .toRight("missing 'program' field from launch request")
-            .flatMap(path =>
-              Either
-                .catchNonFatal(Paths.get(path.getAsString))
-                .leftMap(t => s"'program' field from launch request is not a 
valid path: $t")
-                .ensureOr(path => s"program file at $path doesn't 
exist")(_.toFile().exists())
-            )
-            .toEitherNel,
-          Option(arguments.getAsJsonPrimitive("data"))
-            .toRight("missing 'data' field from launch request")
-            .flatMap(path =>
-              Either
-                .catchNonFatal(Paths.get(path.getAsString))
-                .leftMap(t => s"'data' field from launch request is not a 
valid path: $t")
-                .ensureOr(path => s"data file at $path doesn't 
exist")(_.toFile().exists())
-            )
-            .toEitherNel,
-          Option(arguments.getAsJsonPrimitive("stopOnEntry"))
-            .map(_.getAsBoolean())
-            .getOrElse(true)
-            .asRight[String]
-            .toEitherNel,
-          Option(arguments.getAsJsonObject("infosetOutput")) match {
+          parseProgram(arguments),
+          parseData(arguments),
+          parseStopOnEntry(arguments),
+          parseInfosetOutput(arguments)
+        ).parMapN(LaunchArgs.Manual.apply)
+    }
+
+    // Parse a tdmlConfig object from the launch config
+    //
+    // tdmlConfig: {
+    //   action: '',
+    //   name: '',
+    //   description: '',
+    //   path: ''
+    // }
+    //
+    // The action field is parsed first.
+    // If it is a valid action ('generate' | 'append' | 'execute'), create a 
LaunchArgs object of the appropriate type
+    // If it is 'none' or missing, create a LaunchArgs.Manual object. This 
will ignore any other fields in the tdmlConfig object.
+    //
+    // arguments:  Launch config
+    // tdmlConfig: tdmlConfig object from the launch config
+    def parseTDML(arguments: JsonObject, tdmlConfig: JsonObject): 
EitherNel[String, LaunchArgs] =
+      Option(tdmlConfig.getAsJsonPrimitive("action")) match {
+        case None =>
+          (
+            parseProgram(arguments),
+            parseData(arguments),
+            parseStopOnEntry(arguments),
+            parseInfosetOutput(arguments)
+          ).parMapN(LaunchArgs.Manual.apply)
+        case Some(action) =>
+          action.getAsString() match {
+            case "generate" =>
+              (
+                parseProgram(arguments),
+                parseData(arguments),
+                parseStopOnEntry(arguments),
+                parseInfosetOutput(arguments, true),
+                parseTDMLName(tdmlConfig),
+                parseTDMLDescription(tdmlConfig),
+                parseTDMLPath(tdmlConfig)
+              ).parMapN(LaunchArgs.TDMLConfig.Generate.apply)
+            case "append" =>
+              (
+                parseProgram(arguments),
+                parseData(arguments),
+                parseStopOnEntry(arguments),
+                parseInfosetOutput(arguments, true),
+                parseTDMLName(tdmlConfig),
+                parseTDMLDescription(tdmlConfig),
+                parseTDMLPath(tdmlConfig)
+              ).parMapN(LaunchArgs.TDMLConfig.Append.apply)
+            case "execute" =>
+              (
+                parseStopOnEntry(arguments),
+                parseInfosetOutput(arguments),
+                parseTDMLName(tdmlConfig),
+                parseTDMLDescription(tdmlConfig),
+                parseTDMLPath(tdmlConfig)
+              ).parMapN(LaunchArgs.TDMLConfig.Execute.apply)
+            case "none" =>
+              (
+                parseProgram(arguments),
+                parseData(arguments),
+                parseStopOnEntry(arguments),
+                parseInfosetOutput(arguments)
+              ).parMapN(LaunchArgs.Manual.apply)
+            case invalidType =>
+              Left(
+                s"invalid 'tdmlConfig.action': '$invalidType', must be 'none', 
'generate', 'append', or 'execute'"
+              ).toEitherNel
+          }
+      }
+
+    // Parse the program field from the launch config
+    // Returns an error if the program field is missing or is an invalid path
+    // A case where the user expects a new directory to be created is not a 
valid path
+    //   eg. /path/to/<existing>/<non-existing>/file.tdml
+    //
+    // arguments: Launch config
+    def parseProgram(arguments: JsonObject) =
+      Option(arguments.getAsJsonPrimitive("program"))
+        .toRight("missing 'program' field from launch request")
+        .flatMap(path =>
+          Either
+            .catchNonFatal(Paths.get(path.getAsString))
+            .leftMap(t => s"'program' field from launch request is not a valid 
path: $t")
+            .ensureOr(path => s"program file at $path doesn't 
exist")(_.toFile().exists())
+        )
+        .toEitherNel
+
+    // Parse the data field from the launch config
+    // Returns an error if the data field is missing or is an invalid path
+    // A case where the user expects a new directory to be created is not a 
valid path
+    //   eg. /path/to/<existing>/<non-existing>/file.tdml
+    //
+    // arguments: Launch config
+    def parseData(arguments: JsonObject) =
+      Option(arguments.getAsJsonPrimitive("data"))
+        .toRight("missing 'data' field from launch request")
+        .flatMap(path =>
+          Either
+            .catchNonFatal(Paths.get(path.getAsString))
+            .leftMap(t => s"'data' field from launch request is not a valid 
path: $t")
+            .ensureOr(path => s"data file at $path doesn't 
exist")(_.toFile().exists())
+        )
+        .toEitherNel
+
+    // Parse the stopOnEntry field from the launch config
+    // Defaults to true
+    //
+    // arguments: Launch config
+    def parseStopOnEntry(arguments: JsonObject) =
+      Option(arguments.getAsJsonPrimitive("stopOnEntry"))
+        .map(_.getAsBoolean())
+        .getOrElse(true)
+        .asRight[String]
+        .toEitherNel
+
+    // Parse the infosetOutput object from the launch config
+    //
+    // infosetOutput: {
+    //   type: '',
+    //   path: ''
+    // }
+    //
+    // Type must be 'none' | 'console' | 'file'
+    // If type is 'file', there must be a 'path' field that contains a valid 
path
+    //   for the resulting infoset to be written to
+    // A case where the user expects a new directory to be created is not a 
valid path
+    //   eg. /path/to/<existing>/<non-existing>/file.tdml
+    //
+    // arguments:   Launch config
+    // requireFile: Whether or not the type field must be set to file. This is 
a requirement
+    //              for the TDML generate operation. Returns an error if this 
boolean
+    //              is set to True, and the type field is set to a value other 
than file
+    def parseInfosetOutput(arguments: JsonObject, requireFile: Boolean = 
false) =
+      Option(arguments.getAsJsonObject("infosetOutput")) match {
+        case None => Right(LaunchArgs.InfosetOutput.Console).toEitherNel
+        case Some(infosetOutput) =>
+          Option(infosetOutput.getAsJsonPrimitive("type")) match {
             case None => Right(LaunchArgs.InfosetOutput.Console).toEitherNel
-            case Some(infosetOutput) =>
-              Option(infosetOutput.getAsJsonPrimitive("type")) match {
-                case None => 
Right(LaunchArgs.InfosetOutput.Console).toEitherNel
-                case Some(typ) =>
-                  typ.getAsString() match {
-                    case "none"    => 
Right(LaunchArgs.InfosetOutput.None).toEitherNel
-                    case "console" => 
Right(LaunchArgs.InfosetOutput.Console).toEitherNel
-                    case "file" =>
-                      Option(infosetOutput.getAsJsonPrimitive("path"))
-                        .toRight("missing 'infosetOutput.path' field from 
launch request")
-                        .flatMap(path =>
-                          Either
-                            
.catchNonFatal(LaunchArgs.InfosetOutput.File(Paths.get(path.getAsString)))
-                            .leftMap(t => s"'infosetOutput.path' field from 
launch request is not a valid path: $t")
-                            .ensureOr(file => s"can't write to infoset output 
file at ${file.path}") { f =>
-                              val file = f.path.toFile
-                              file.canWrite || (!file.exists && 
file.getParentFile.canWrite)
-                            }
-                        )
-                        .toEitherNel
-                    case invalidType =>
-                      Left(
-                        s"invalid 'infosetOutput.type': '$invalidType', must 
be 'none', 'console', or 'file'"
-                      ).toEitherNel
-                  }
+            case Some(typ) =>
+              typ.getAsString() match {
+                case "none" =>
+                  if (requireFile)
+                    Left("'type' field in 'infosetOutput' must be set to 
'file'").toEitherNel
+                  else
+                    Right(LaunchArgs.InfosetOutput.None).toEitherNel
+                case "console" =>
+                  if (requireFile)
+                    Left("'type' field in 'infosetOutput' must be set to 
'file'").toEitherNel
+                  else
+                    Right(LaunchArgs.InfosetOutput.Console).toEitherNel
+                case "file" =>
+                  Option(infosetOutput.getAsJsonPrimitive("path"))
+                    .toRight("missing 'infosetOutput.path' field from launch 
request")
+                    .flatMap(path =>
+                      Either
+                        
.catchNonFatal(LaunchArgs.InfosetOutput.File(Paths.get(path.getAsString)))
+                        .leftMap(t => s"'infosetOutput.path' field from launch 
request is not a valid path: $t")
+                        .ensureOr(file => s"can't write to infoset output file 
at ${file.path}") { f =>
+                          val file = f.path.toFile
+                          // If an empty string is passed in, it will be set 
to the workspace directory by default
+                          // This is inside the Java code, so we have to make 
sure that the TDML file we
+                          //   are working with is not a directory
+                          !file.isDirectory() && (file.canWrite || 
(!file.exists && file.getParentFile.canWrite))
+                        }
+                    )
+                    .toEitherNel
+                case invalidType =>
+                  Left(
+                    s"invalid 'infosetOutput.type': '$invalidType', must be 
'none', 'console', or 'file'"
+                  ).toEitherNel
               }
           }
-        ).parMapN(LaunchArgs.apply)
-    }
+      }
+
+    // The following functions granularly parse the tdmlConfig object from the 
launch config
+    //
+    // tdmlConfig: {
+    //   action: '',
+    //   name: '',
+    //   description: '',
+    //   path: ''
+    // }
+    //
+    // The action field is parsed elsewhere. If these functions are hit, a 
valid action
+    //   other than 'none' was found.
+
+    // Parse the  name field from the tdmlConfig object from the launch config
+    // Returns an error if the field is missing or is an empty string
+    //
+    // tdmlConfig: tdmlConfig object from the launch config
+    def parseTDMLName(tdmlConfig: JsonObject) =
+      Option(tdmlConfig.getAsJsonPrimitive("name"))
+        .toRight("missing 'tdmlConfig.name' field from launch request")
+        .map(_.getAsString())
+        .flatMap(name => Either.cond(name.length() > 0, name, "'name' field 
from 'tdmlConfig' object cannot be empty"))
+        .toEitherNel
+
+    // Parse the description field from the tdmlConfig object from the launch 
config
+    // Returns an error if the field is missing or is an empty string
+    //
+    // tdmlConfig: tdmlConfig object from the launch config
+    def parseTDMLDescription(tdmlConfig: JsonObject) =
+      Option(tdmlConfig.getAsJsonPrimitive("description"))
+        .toRight("missing 'tdmlConfig.description' field from launch request")
+        .map(_.getAsString())
+        .flatMap(description =>
+          Either
+            .cond(description.length() > 0, description, "'description' field 
from 'tdmlConfig' object cannot be empty")
+        )
+        .toEitherNel
+
+    // Parse the path field from the tdmlConfig object from the launch config
+    // Returns an error if the field is missing or is an invalid path
+    // A case where the user expects a new directory to be created is not a 
valid path
+    //   eg. /path/to/<existing>/<non-existing>/file.tdml
+    //
+    // tdmlConfig: tdmlConfig object from the launch config
+    def parseTDMLPath(tdmlConfig: JsonObject) =
+      Option(tdmlConfig.getAsJsonPrimitive("path"))
+        .toRight("missing 'tdmlConfig.path' field from launch request")
+        .flatMap(path =>
+          Either
+            
.catchNonFatal(Paths.get(path.getAsString).toFile().getAbsolutePath())
+            .leftMap(t => s"'infosetOutput.path' field from launch request is 
not a valid path: $t")
+            .ensureOr(file => s"can't write to infoset output file at 
${file}") { f =>
+              val file = Paths.get(f).toFile()
+              // If an empty string is passed in, it will be set to the 
workspace directory by default
+              // This is inside the Java code, so we have to make sure that 
the TDML file we
+              //   are working with is not a directory
+              !file.isDirectory() && (file.canWrite || (!file.exists && 
file.getParentFile.canWrite))
+            }
+        )
+        .toEitherNel

Review Comment:
   I'm a little concerned that this duplicates a decent amount of logic from 
the daffodil tdml library. And there's a lot of non trivial code in the TDML 
library that isn't implemented that we definitely don't want to reimplement. 
For example, input data can be defined in binary, hex, or characters, and even 
more complicates like bitOrder, replaceDFDLEntitites, etc. And this is just the 
data portion. There's a lot of complexity to TDML files that I would hate to 
have duplicated.



##########
resources/xsd/DFDL_part3_model.xsd:
##########
@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="UTF-8"?>

Review Comment:
   I assume all these files in `resources/xsd` have just been copied and 
unmodified from Daffodil? Can they instead be pulled out of the the 
daffodil-lib dependency?
   
   Otherwise these files may start to drift from the ones in Daffodil when we 
make changes and we'll have to remember (and most likely forget) to keep things 
updated.



-- 
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]

Reply via email to