stevedlawrence commented on code in PR #183:
URL: https://github.com/apache/daffodil-sbt/pull/183#discussion_r3236278181


##########
src/main/scala/org/apache/daffodil/DaffodilPlugin.scala:
##########
@@ -840,6 +854,179 @@ object DaffodilPlugin extends AutoPlugin {
     }
   }
 
+  def daffodilFlattenSettings: Seq[Setting[_]] = Seq(
+    daffodilFlattenTarget := target.value / 
s"${name.value}-${version.value}-flat.zip",
+
+    /**
+     * Whether or not to publish the flattened schemas zip. Defaults to false.
+     *
+     * If projects want to publish flattened schemas then they must explicitly 
enable it by
+     * setting 'daffodilFlattenSchemas / publishArtifact := true'.
+     *
+     * If false, flattened schemas will not be created unless you explicitly 
run the
+     * daffodilFlattenSchemas.
+     */
+    daffodilFlattenSchemas / publishArtifact := false,
+
+    daffodilFlattenSchemas / artifact := Artifact(
+      name.value,
+      "flat",
+      "zip",
+      Some("flat"),
+      Vector(),
+      None
+    ),
+
+    /**
+     * Only grab the file types that need to be flattened. These files are only
+     * grabbed from the root project. Any referenced files will be pulled in
+     * from the classpath. Note that XML files are commonly used in XSLT for
+     * settings.
+     */
+    daffodilFlattenSchemas / includeFilter := "*.xsd" | "*.xsl" | "*.xslt" | 
"*.xml",
+    daffodilFlattenSchemas / excludeFilter := HiddenFileFilter,
+
+    daffodilFlattenResourceReferencePatterns := List(
+        """schemaLocation=\"([^\"]*)\"""".r,
+        "href=\"([^\"]*)\"".r,
+        "document[(]'([^']*)'[)]".r),
+
+    daffodilFlattenSchemas / products := {
+
+      val logger = streams.value.log
+      val filter = (daffodilFlattenSchemas / includeFilter).value -- 
(daffodilFlattenSchemas / excludeFilter).value
+
+      val flatDir = target.value / "flatDir"
+      if (flatDir.exists())
+        IO.delete(flatDir)
+      IO.createDirectory(flatDir)
+
+      val projectResources = (Compile / resourceDirectories).value
+        .map { root => root.toURI.toURL -> (root ** 
filter).get.map(_.toURI.toURL).toList }
+        .toMap
+
+      /**
+       * Create a URLClassLoader object with URLs to all resources used by the
+       * project. The class loader will be used to resolve references made
+       * within the flattened files.
+       */
+      val projectURLs = (Compile / 
resourceDirectories).value.map(_.toURI.toURL)
+      val allClasspathURLs = (Test / 
externalDependencyClasspath).value.map(_.data.toURI.toURL)
+      val classLoader = new URLClassLoader((projectURLs ++ 
allClasspathURLs).toArray, null)
+
+      val referenceRegexes = daffodilFlattenResourceReferencePatterns.value
+
+      val processed = scala.collection.mutable.Set[URI]()
+      projectResources foreach { case (rootURL, urls) =>
+        val unprocessed = scala.collection.mutable.Stack[URI]()
+        val rootURI = rootURL.toURI
+        val rootPath = Paths.get(rootURI)
+        unprocessed.pushAll(urls.map(_.toURI))
+        while (!unprocessed.isEmpty) {
+          val contextURI = unprocessed.pop
+          val bytes = contextURI.toURL.openStream().readAllBytes()
+          val contextRelPath = contextURI.getScheme match {
+            case "jar" => Paths.get(contextURI.toString.split("!")(1).tail)
+            case "file" => Paths.get(rootURI.relativize(contextURI).getPath)
+            case _ => throw new IllegalArgumentException(s"Unrecognized URI 
scheme: $contextURI")
+          }
+          val contextFlatPath = Paths.get(
+            flatDir.toString,
+            contextRelPath.toString.replaceAll("/", "__"))
+          val bw = Files.newBufferedWriter(contextFlatPath)
+          val fileAsString = new String(bytes, Charset.defaultCharset())
+
+          val references = referenceRegexes.flatMap { re =>
+            re.findAllIn(fileAsString).matchData.map(_.group(1))
+          }
+
+          val resolvedRefs = references.map { ref =>
+            val origLocation = if (ref contains " ") ref.split(" ")(1) else ref
+            if (origLocation.startsWith("/")) {
+              // Dealing with an absolute path
+              val url = Option(classLoader.findResource(origLocation.tail))
+              if (url.isDefined)
+                Some(origLocation -> url.get.toURI)
+              else {
+                logger.warn(s"Unable to resolve absolute reference to $ref 
from source file $contextURI")
+                None
+              }
+            } else {
+              // Relative path
+              val relPath = 
contextRelPath.resolveSibling(origLocation).normalize()
+              if (Files.exists(rootPath.resolve(relPath))) {

Review Comment:
   The convention that Daffodil implements is that relative paths can only 
refer to files in the same jar and absolute paths must be used to reference 
files in another jar.
   
   So in the case of Jreap, it defines that a payload schema must exist at a 
specific path, and it does so using an absolute path so that it can reference a 
file in another jar, like in the link16 jar. And then the link16 jar is 
responsible for containing that payload file at the absolute path that jreap 
expects. 
   
   Note that jreap doesn't actually define a payload schema in src/main/schema, 
it just says where it must exist as provided by another jar. It does have a 
payload schema it uses for testing, but that is in src/main/test so isn't 
reachable by jars that depend on it like link16.
   
   



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