This is an automated email from the ASF dual-hosted git repository.
mbeckerle 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 48b3f70c8 Changes for Java 21 - a LTS Java release
48b3f70c8 is described below
commit 48b3f70c8d185afde42f37b2deb5e44ad4ee5525
Author: Michael Beckerle <[email protected]>
AuthorDate: Sat Sep 23 14:03:38 2023 -0400
Changes for Java 21 - a LTS Java release
Required changes due to deprecated objects and methods.
java.net.URL all constructors were deprecated.
This was harder than one would expect. There is no direct replacement
in the java.net library for the URL constructors. Nothing tolerates
jar-file URIs as of Java 20+.
Add xsi:type sensitive comparison for float and double.
Tolerate regex match behavior change: seems lookahead is no longer
captured into individual groups.
Now uses Java 11 release for Java code compilation with Java 21.
This eliminates warnings about Java 8 being deprecated.
Removed redundancy of XMLUtils and NodeInfo for converting strings to
float/double.
DAFFODIL-2757, DAFFODIL-2402
---
build.sbt | 32 +++--
.../main/scala/org/apache/daffodil/cli/Main.scala | 8 +-
.../apache/daffodil/core/api/TestForHeapDump.scala | 2 -
.../apache/daffodil/layers/TestJavaIOStreams.scala | 12 +-
.../scala/org/apache/daffodil/lib/util/Misc.scala | 118 ++++++++++++-----
.../org/apache/daffodil/lib/xml/XMLUtils.scala | 142 ++++++++++++++++-----
.../daffodil/lib/xml/test/unit/TestXMLUtils.scala | 52 +++++++-
.../apache/daffodil/runtime1/dpath/NodeInfo.scala | 16 +--
.../test-suite/tresys-contributed/BG.tdml | 25 ++--
.../section05/simple_types/SimpleTypes.tdml | 2 +-
.../text_number_props/TextNumberProps.tdml | 4 +-
11 files changed, 297 insertions(+), 116 deletions(-)
diff --git a/build.sbt b/build.sbt
index 9809bd5a5..1c01d8b7f 100644
--- a/build.sbt
+++ b/build.sbt
@@ -211,6 +211,12 @@ lazy val testStdLayout =
Project("daffodil-test-stdLayout", file("test-stdLayout
.dependsOn(tdmlProc % "test")
.settings(commonSettings, nopublish)
+// Choices here are Java LTS versions, 8, 11, 17, 21,...
+// However 8 is deprecated as of Java 21, so will be phased out.
+val minSupportedJavaVersion: String =
+ if (scala.util.Properties.isJavaAtLeast("21")) "11"
+ else "8"
+
lazy val commonSettings = Seq(
organization := "org.apache.daffodil",
version := "3.6.0-SNAPSHOT",
@@ -245,7 +251,7 @@ lazy val commonSettings = Seq(
def buildScalacOptions(scalaVersion: String) = {
val commonOptions = Seq(
- "-target:jvm-1.8",
+ s"-release:$minSupportedJavaVersion", // scala 2.12 can only do Java 8,
regardless of this setting.
"-feature",
"-deprecation",
"-language:experimental.macros",
@@ -268,13 +274,19 @@ def buildScalacOptions(scalaVersion: String) = {
case _ => Seq.empty
}
- val javaVersionSpecificOptions =
- if (scala.util.Properties.isJavaAtLeast("9"))
- Seq("-release", "8") // ensure Java backwards compatibility
(DAFFODIL-2579)
- else
- Seq.empty
+ commonOptions ++ scalaVersionSpecificOptions
+}
+
+val javaVersionSpecificOptions = {
+ val releaseOption = // as of Java 11, they no longer accept "-release". Must
use "--release".
+ if (scala.util.Properties.isJavaAtLeast("11")) "--release" else "-release"
- commonOptions ++ scalaVersionSpecificOptions ++ javaVersionSpecificOptions
+ // Java 21 deprecates Java 8 and warns about it.
+ // So if you are using Java 21, Java code compilation will specify a newer
Java version
+ // to avoid warnings.
+ if (scala.util.Properties.isJavaAtLeast("11")) Seq(releaseOption,
minSupportedJavaVersion)
+ else if (scala.util.Properties.isJavaAtLeast("9")) Seq(releaseOption, "8")
+ else Nil // for Java 8 compilation
}
// Workaround issue that some options are valid for javac, not javadoc.
@@ -285,12 +297,6 @@ def buildJavacOptions() = {
"-Xlint:deprecation",
)
- val javaVersionSpecificOptions =
- if (scala.util.Properties.isJavaAtLeast("9"))
- Seq("--release", "8") // ensure Java backwards compatibility
(DAFFODIL-2579)
- else
- Seq.empty
-
commonOptions ++ javaVersionSpecificOptions
}
diff --git a/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala
b/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala
index 80409ac30..5ecc7c3d9 100644
--- a/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala
+++ b/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala
@@ -28,6 +28,7 @@ import java.nio.channels.Channels
import java.nio.file.Paths
import java.util.Scanner
import java.util.concurrent.Executors
+import javax.xml.parsers.SAXParserFactory
import javax.xml.transform.TransformerException
import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamResult
@@ -92,7 +93,6 @@ import org.rogach.scallop.exceptions.GenericScallopException
import org.slf4j.event.Level
import org.xml.sax.InputSource
import org.xml.sax.SAXParseException
-import org.xml.sax.helpers.XMLReaderFactory
class ScallopExitException(val exitCode: Int) extends Exception
@@ -1813,8 +1813,10 @@ class Main(
case (false, true) => { // Encoding
val exiResult = new EXIResult(exiFactory.get)
exiResult.setOutputStream(output)
-
- val reader = XMLReaderFactory.createXMLReader()
+ val factory = SAXParserFactory.newInstance()
+ factory.setNamespaceAware(true)
+ val saxParser = factory.newSAXParser()
+ val reader = saxParser.getXMLReader
reader.setContentHandler(exiResult.getHandler)
reader.setErrorHandler(new EXIErrorHandler)
try {
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/core/api/TestForHeapDump.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/core/api/TestForHeapDump.scala
index e0ddf4f59..c9868234b 100644
---
a/daffodil-core/src/test/scala/org/apache/daffodil/core/api/TestForHeapDump.scala
+++
b/daffodil-core/src/test/scala/org/apache/daffodil/core/api/TestForHeapDump.scala
@@ -150,8 +150,6 @@ class TestForHeapDump {
}
def gcAndAllowHeapDump(): Unit = {
- System.gc()
- System.runFinalization()
System.gc()
System.out.println("Take a Heap Dump Now! (You have 10 seconds)")
Thread.sleep(10000)
diff --git
a/daffodil-io/src/test/scala/org/apache/daffodil/layers/TestJavaIOStreams.scala
b/daffodil-io/src/test/scala/org/apache/daffodil/layers/TestJavaIOStreams.scala
index 0b9a277b7..eba14b049 100644
---
a/daffodil-io/src/test/scala/org/apache/daffodil/layers/TestJavaIOStreams.scala
+++
b/daffodil-io/src/test/scala/org/apache/daffodil/layers/TestJavaIOStreams.scala
@@ -129,10 +129,16 @@ ZyBzb2x1dGlvbnMuCg=="""
val scanner = new Scanner(is, StandardCharsets.ISO_8859_1.name())
is.skip(3)
is.mark(2)
- val matchString = scanner.findWithinHorizon("(.*?)(?=(\\Q;\\E))", 2)
+ // Prior to changes for Java21, this test used non-capturing lookahead for
the ";"
+ // and group(2) was containing the match of the lookahead.
+ // as of Java21 testing, the lookahead match is no longer made into a
group it seems.
+ // The lookahead is included in the "whole match" aka group(0)
+ val matchString = scanner.findWithinHorizon("(.*?)(\\Q;\\E)", 2)
is.reset()
- assertEquals("l", matchString)
- assertEquals(";", scanner.`match`().group(2))
+ val m = scanner.`match`()
+ assertEquals("l;", m.group(0))
+ assertEquals("l", m.group(1))
+ assertEquals(";", m.group(2))
}
/**
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala
index 11ef999d5..70103e6f0 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala
@@ -20,9 +20,8 @@ package org.apache.daffodil.lib.util
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
-import java.io.InputStream
+import java.io.IOException
import java.net.URI
-import java.net.URL
import java.net.URLClassLoader
import java.nio.ByteBuffer
import java.nio.CharBuffer
@@ -103,38 +102,102 @@ object Misc {
// more time is wasted by people forgetting that the initial "/" is needed
// to get classpath relative behavior... Let's make sure there is a
leading "/"
val resPath = if (resourcePath.startsWith("/")) resourcePath else "/" +
resourcePath
- val res = this.getClass().getResource(resPath)
- if (res == null) {
- (None, resPath)
- } else (Some(res.toURI), resPath)
+ val res = Option(this.getClass().getResource(resPath))
+ (res.map(_.toURI), resPath)
}
/**
* Gets a resource on the classpath, or relative to another URI
*/
- def getResourceRelativeOption(rawResName: String, optContextURI:
Option[URI]): Option[URI] = {
+ private def getResourceAbsoluteOrRelativeOption(
+ rawResName: String,
+ optContextURI: Option[URI],
+ ): Option[URI] = {
val resName = rawResName.replaceAll("""\s""", "%20")
val (maybeRes, _) = Misc.getResourceOption(resName)
if (maybeRes.isDefined) {
maybeRes // found directly on the classpath.
} else {
optContextURI.flatMap { contextURI =>
- //
- // try relative to enclosing context uri
- //
- // Done using URL constructor because the URI.resolve(uri) method
- // doesn't work against so called opaque URIs, and jar URIs of the
- // sort we get here if the resource is in a jar, are opaque.
- // Some discussion of this issue is
https://issues.apache.org/jira/browse/XMLSCHEMA-3
- //
- val contextURL = contextURI.toURL
- val completeURL = new URL(contextURL, resName)
- val res = tryURL(completeURL)
- res
+ getResourceRelativeOnlyOption(resName, contextURI)
}
}
}
+ /**
+ * Get resource relative to the context URI.
+ *
+ * Does NOT try the string as an absolute location first
+ * or anything like that.
+ *
+ * @param relPath
+ * @param contextURI
+ * @return Some uri if the relative resource exists.
+ */
+ def getResourceRelativeOnlyOption(relPath: String, contextURI: URI):
Option[URI] = {
+ Assert.usage(relPath ne null)
+ Assert.usage(contextURI ne null)
+ if (contextURI.isOpaque) {
+ //
+ // We used to call new URL(jarURI, relativePathString)
+ // but that is deprecated now (as of Java 20)
+ //
+ optRelativeJarFileURI(contextURI, relPath)
+ } else {
+ // context URI is not opaque. It's probably a file URI
+ if (contextURI.getScheme == "file") {
+ val relURI = contextURI.resolve(relPath)
+ if (Paths.get(relURI).toFile.exists())
+ Some(relURI)
+ else None
+ } else {
+ // not a file nor an opaque resource URI. What is it?
+ throw new IllegalArgumentException(s"Unrecognized URI type:
$contextURI")
+ }
+ }
+ }
+
+ /**
+ * Java 20 deprecated the 2-arg URL constructor which worked to create
relative URIs
+ * within the same Jar file.
+ *
+ * This is a bit harder to achieve now. You are not allowed to resolve
relative to a jar file URI.
+ * That is URI.resolve(relPath) doesn't work if the URI is a jar file URI.
+ *
+ * Now we have to hack the jar:file: URI as a string because URI.resolve
won't work
+ *
+ * jar file URIs look like this:
+ *
+ * `jar:file:/..absolute path to jar file.jar!/absolute path from root
inside jar to file``
+ *
+ * We split at the !/, make a relative path on just the inside-jar-file
part, then glue
+ * back together.
+ *
+ *
+ * @param contextURI
+ * @param relPath
+ * @return Some(uri) for an existing relative path within the same jar file,
or None if it does not exist.
+ */
+ def optRelativeJarFileURI(contextURI: URI, relPath: String): Option[URI] = {
+ val parts = contextURI.toString.split("\\!\\/")
+ Assert.invariant(parts.length == 2)
+ val jarPart = parts(0)
+ val pathPart = parts(1)
+ Assert.invariant(pathPart ne null)
+ val contextURIPathOnly = URI.create(pathPart)
+ val resolvedURIPathOnly = contextURIPathOnly.resolve(relPath)
+ val newJarPathURI = URI.create(jarPart + "!/" +
resolvedURIPathOnly.toString)
+ try {
+ newJarPathURI.toURL.openStream().close()
+ // that worked, so we can open it so it exists.
+ Some(newJarPathURI)
+ } catch {
+ case io: IOException =>
+ // failed. So that jar file doesn't exist
+ None
+ }
+ }
+
/**
* Search for a resource name, trying a handful of heuristics.
*
@@ -155,7 +218,7 @@ object Misc {
if (resAsURI.getScheme != null) Paths.get(resAsURI) else
Paths.get(resName)
val resolvedURI =
if (Files.exists(resPath)) Some(resPath.toFile().toURI())
- else Misc.getResourceRelativeOption(resName, relativeTo)
+ else Misc.getResourceAbsoluteOrRelativeOption(resName, relativeTo)
val res = resolvedURI.orElse {
// try ignoring the directory part
val parts = resName.split("/")
@@ -170,21 +233,6 @@ object Misc {
res
}
- private def tryURL(url: URL): Option[URI] = {
- var is: InputStream = null
- val res =
- try {
- is = url.openStream()
- // worked! We found it.
- Some(url.toURI)
- } catch {
- case e: java.io.IOException => None
- } finally {
- if (is != null) is.close()
- }
- res
- }
-
lazy val classPath = {
val cl = this.getClass().getClassLoader()
val urls = cl match {
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/lib/xml/XMLUtils.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/lib/xml/XMLUtils.scala
index 0a1d88b61..176bbda56 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/lib/xml/XMLUtils.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/lib/xml/XMLUtils.scala
@@ -21,7 +21,6 @@ import java.io.File
import java.io.IOException
import java.net.URI
import java.net.URISyntaxException
-import java.net.URL
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
@@ -30,6 +29,7 @@ import javax.xml.XMLConstants
import scala.annotation.tailrec
import scala.collection.mutable
import scala.collection.mutable.ArrayBuilder
+import scala.math.abs
import scala.util.matching.Regex
import scala.xml.NamespaceBinding
import scala.xml._
@@ -70,6 +70,34 @@ object XMLUtils {
val NegativeInfinityString = "-INF"
val NaNString = "NaN"
+ /**
+ * Converts a string to a float, including handling our INF, -INF, and NaN
notations.
+ * @param s
+ * @return
+ */
+ def strToFloat(s: String): Float = {
+ s match {
+ case PositiveInfinityString => Float.PositiveInfinity
+ case NegativeInfinityString => Float.NegativeInfinity
+ case NaNString => Float.NaN
+ case _ => s.toFloat
+ }
+ }
+
+ /**
+ * Converts a string to a double, including handling our INF, -INF, and NaN
notations.
+ * @param s
+ * @return
+ */
+ def strToDouble(s: String): Double = {
+ s match {
+ case PositiveInfinityString => Double.PositiveInfinity
+ case NegativeInfinityString => Double.NegativeInfinity
+ case NaNString => Double.NaN
+ case _ => s.toDouble
+ }
+ }
+
/**
* Length where a surrogate pair counts as 1 character, not two.
*/
@@ -778,6 +806,8 @@ object XMLUtils {
ignoreProcInstr: Boolean = true,
checkPrefixes: Boolean = false,
checkNamespaces: Boolean = false,
+ maybeFloatEpsilon: Option[Float] = None,
+ maybeDoubleEpsilon: Option[Double] = None,
): Unit = {
val expectedMinimized = normalize(expected)
val actualMinimized = normalize(actual)
@@ -787,6 +817,8 @@ object XMLUtils {
ignoreProcInstr,
checkPrefixes,
checkNamespaces,
+ maybeFloatEpsilon,
+ maybeDoubleEpsilon,
)
if (diffs.length > 0) {
throw new XMLDifferenceException(
@@ -821,6 +853,8 @@ Differences were (path, expected, actual):
ignoreProcInstr: Boolean = true,
checkPrefixes: Boolean = false,
checkNamespaces: Boolean = false,
+ maybeFloatEpsilon: Option[Float] = None,
+ maybeDoubleEpsilon: Option[Double] = None,
) = {
computeDiffOne(
a,
@@ -833,6 +867,8 @@ Differences were (path, expected, actual):
checkPrefixes,
checkNamespaces,
None,
+ maybeFloatEpsilon,
+ maybeDoubleEpsilon,
)
}
@@ -850,6 +886,11 @@ Differences were (path, expected, actual):
arrayCounters
}
+ private def getXSIType(a: Elem) = {
+ val res = a.attribute(XSI_NAMESPACE.toString, "type").map(_.head.text)
+ res
+ }
+
def computeDiffOne(
an: Node,
bn: Node,
@@ -861,14 +902,16 @@ Differences were (path, expected, actual):
checkPrefixes: Boolean,
checkNamespaces: Boolean,
maybeType: Option[String],
+ maybeFloatEpsilon: Option[Float],
+ maybeDoubleEpsilon: Option[Double],
): Seq[(String, String, String)] = {
lazy val zPath = parentPathSteps.reverse.mkString("/")
(an, bn) match {
case (a: Elem, b: Elem) => {
val Elem(prefixA, labelA, attribsA, nsbA, childrenA @ _*) = a
val Elem(prefixB, labelB, attribsB, nsbB, childrenB @ _*) = b
- val typeA: Option[String] = a.attribute(XSI_NAMESPACE.toString,
"type").map(_.head.text)
- val typeB: Option[String] = b.attribute(XSI_NAMESPACE.toString,
"type").map(_.head.text)
+ val typeA: Option[String] = getXSIType(a)
+ val typeB: Option[String] = getXSIType(b)
val maybeType: Option[String] =
Option(typeA.getOrElse(typeB.getOrElse(null)))
val nilledA = a.attribute(XSI_NAMESPACE.toString, "nil")
val nilledB = b.attribute(XSI_NAMESPACE.toString, "nil")
@@ -941,6 +984,8 @@ Differences were (path, expected, actual):
checkPrefixes,
checkNamespaces,
maybeType,
+ maybeFloatEpsilon,
+ maybeDoubleEpsilon,
)
}
@@ -963,13 +1008,14 @@ Differences were (path, expected, actual):
}
}
case (tA: Text, tB: Text) => {
- val thisDiff = computeTextDiff(zPath, tA, tB, maybeType)
+ val thisDiff =
+ computeTextDiff(zPath, tA, tB, maybeType, maybeFloatEpsilon,
maybeDoubleEpsilon)
thisDiff
}
case (pA: ProcInstr, pB: ProcInstr) => {
val ProcInstr(tA1label, tA1content) = pA
val ProcInstr(tB1label, tB1content) = pB
- val labelDiff = computeTextDiff(zPath, tA1label, tB1label, None)
+ val labelDiff = computeTextDiff(zPath, tA1label, tB1label, None, None,
None)
//
// The content of a ProcInstr is technically a big string
// But our usage of them the content is XML-like so could be loaded
and then compared
@@ -981,7 +1027,7 @@ Differences were (path, expected, actual):
//
// TODO: implement XML-comparison for our data format info PIs.
//
- val contentDiff = computeTextDiff(zPath, tA1content, tB1content,
maybeType)
+ val contentDiff = computeTextDiff(zPath, tA1content, tB1content,
maybeType, None, None)
labelDiff ++ contentDiff
}
case _ => {
@@ -995,11 +1041,13 @@ Differences were (path, expected, actual):
tA: Text,
tB: Text,
maybeType: Option[String],
+ maybeFloatEpsilon: Option[Float],
+ maybeDoubleEpsilon: Option[Double],
): Seq[(String, String, String)] = {
val dataA = tA.toString
val dataB = tB.toString
- computeTextDiff(zPath, dataA, dataB, maybeType)
+ computeTextDiff(zPath, dataA, dataB, maybeType, maybeFloatEpsilon,
maybeDoubleEpsilon)
}
def computeBlobDiff(zPath: String, dataA: String, dataB: String) = {
@@ -1061,12 +1109,14 @@ Differences were (path, expected, actual):
dataA: String,
dataB: String,
maybeType: Option[String],
+ maybeFloatEpsilon: Option[Float],
+ maybeDoubleEpsilon: Option[Double],
): Seq[(String, String, String)] = {
val hasBlobType = maybeType.isDefined && maybeType.get == "xs:anyURI"
val dataLooksLikeBlobURI = Seq(dataA,
dataB).forall(_.startsWith("file://"))
if (hasBlobType || dataLooksLikeBlobURI) computeBlobDiff(zPath, dataA,
dataB)
- else if (textIsSame(dataA, dataB, maybeType)) Nil
+ else if (textIsSame(dataA, dataB, maybeType, maybeFloatEpsilon,
maybeDoubleEpsilon)) Nil
else {
// There must be some difference, so let's find just the first index of
// difference and we'll include that and some following characters for
@@ -1095,7 +1145,30 @@ Differences were (path, expected, actual):
}
}
- def textIsSame(dataA: String, dataB: String, maybeType: Option[String]):
Boolean = {
+ /**
+ * Compares two strings of xml text, optionally using type information to
tolerate insignificant differences, and
+ * optionally using a tolerance amount for floating point comparison.
+ *
+ * @param dataA string for first value in comparison
+ * @param dataB string for second value in comparison
+ * @param maybeType - type as a "xs:" prefixed QName string of an XSD type
(Ex: as in the value of
+ * the xsi:type="xs:date" attribute.) Any non "xs:"
prefixed type is ignored.
+ * @param maybeFloatEpsilon - for floating point comparison, a float (single
precision) expressing the acceptable delta
+ * amount to proclaim equality.
+ * @param maybeDoubleEpsilon - for floating point comparison, a double
(double precision) expressing the acceptable delta
+ * amount to proclaim equality.
+ * @return
+ */
+ def textIsSame(
+ dataA: String,
+ dataB: String,
+ maybeType: Option[String],
+ maybeFloatEpsilon: Option[Float],
+ maybeDoubleEpsilon: Option[Double],
+ ): Boolean = {
+ maybeFloatEpsilon.foreach { eps => Assert.usage(eps > 0.0) }
+ maybeDoubleEpsilon.foreach { eps => Assert.usage(eps > 0.0) }
+
maybeType match {
case Some("xs:hexBinary") => dataA.equalsIgnoreCase(dataB)
case Some("xs:date") => {
@@ -1113,6 +1186,29 @@ Differences were (path, expected, actual):
val b = DFDLDateTimeConversion.fromXMLString(dataB)
a == b
}
+ case Some("xs:double") => {
+ val a = strToDouble(dataA)
+ val b = strToDouble(dataB)
+ if (a.isNaN && b.isNaN) true // two NaNs are not normally considered
equal
+ else {
+ maybeDoubleEpsilon match {
+ case None => a == b
+ case Some(epsilon) => abs(a - b) < epsilon
+ }
+ }
+ }
+ case Some("xs:float") => {
+ val a = strToFloat(dataA)
+ val b = strToFloat(dataB)
+ if (a.isNaN && b.isNaN) true // two NaNs are not normally considered
equal
+ else {
+ maybeFloatEpsilon match {
+ case None => a == b
+ case Some(epsilon) => abs(a - b) < epsilon
+ }
+ }
+ }
+
case _ => dataA == dataB
}
}
@@ -1347,7 +1443,7 @@ Differences were (path, expected, actual):
uri.getFragment == null &&
uri.getPath != null
- val optResolved =
+ val optResolved: Option[(URI, Boolean)] =
if (uri.isAbsolute) {
// an absolute URI is one with a scheme. In this case, we expect to be
able to resolve
// the URI and do not try anything else (e.g. filesystem, classpath).
Since this function
@@ -1392,20 +1488,8 @@ Differences were (path, expected, actual):
val contextURI = optContextURI.get
val optResolvedRelative = None
.orElse {
- // This is a relative path, so look up the schemaLocation path
relative to the
- // context. Note that URI.resolve does not support opaque URIs,
and the context
- // parameter is often an opaque "jar" URI if the context resolved
to a file inside a
- // jar on the classpath. Instead we can use the URL constructor to
resolve the
- // relative path, which does support opaque URIs for supported
schemes (jar and
- // file). Unfortunately, the only way to test for URL existence is
to open a stream
- // to that resulting URL and see if an exception is thrown or not
- val resolvedURL = new URL(contextURI.toURL, uri.getPath)
- try {
- resolvedURL.openStream.close
- Some((resolvedURL.toURI, false))
- } catch {
- case e: IOException => None
- }
+ // This is a relative path, so look up the schemaLocation path
relative to the context
+ Misc.getResourceRelativeOnlyOption(uri.getPath, contextURI).map {
(_, false) }
}
.orElse {
// The user might have meant an absolute schemaLocation but left
off the leading
@@ -1413,12 +1497,10 @@ Differences were (path, expected, actual):
// but return a boolean if a relative path was found absolutely so
callers can warn
// if needed. Future versions of Daffodil may want to remove this
orElse block so we
// are strict about how absolute vs relative schemaLocations are
resolved.
- val resource = this.getClass.getResource("/" + uri.getPath)
- if (resource != null) {
- Some((resource.toURI, true))
- } else {
- None
- }
+ val pair = Option(this.getClass.getResource("/" + uri.getPath))
+ .map { _.toURI }
+ .map { (_, true) }
+ pair
}
optResolvedRelative
}
diff --git
a/daffodil-lib/src/test/scala/org/apache/daffodil/lib/xml/test/unit/TestXMLUtils.scala
b/daffodil-lib/src/test/scala/org/apache/daffodil/lib/xml/test/unit/TestXMLUtils.scala
index c833b52b4..f74f672f8 100644
---
a/daffodil-lib/src/test/scala/org/apache/daffodil/lib/xml/test/unit/TestXMLUtils.scala
+++
b/daffodil-lib/src/test/scala/org/apache/daffodil/lib/xml/test/unit/TestXMLUtils.scala
@@ -36,7 +36,7 @@ class TestXMLUtils {
@Test def testDiff0(): Unit = {
val d1 = new Text("a")
val d2 = new Text("b")
- val diffs = XMLUtils.computeTextDiff("", d1, d2, None)
+ val diffs = XMLUtils.computeTextDiff("", d1, d2, None, None, None)
val Seq((p, a, b)) = diffs
assertEquals(".charAt(1)", p)
assertEquals("a", a)
@@ -74,7 +74,6 @@ class TestXMLUtils {
assertEquals("a/d[2].charAt(3)", p2)
assertEquals("x", x)
assertEquals("y", y)
-
}
@Test def testNilDiff1(): Unit = {
@@ -111,6 +110,55 @@ class TestXMLUtils {
assertEquals("xmlns:ns1=\"someprefixB\"", b)
}
+ val xsiNS = "http://www.w3.org/2001/XMLSchema-instance"
+ val xsNS = "http://www.w3.org/2001/XMLSchema"
+
+ @Test def testDoubleDiffNoEpsilon(): Unit = {
+ val d1 = <tnp05 xmlns:xsi={xsiNS} xmlns:xs={xsNS} xsi:type="xs:double">
+ 9.8765432109876544E16
+ </tnp05>
+ val d2 = <tnp05 xmlns:xsi={xsiNS} xmlns:xs={xsNS} xsi:type="xs:double">
+ 9.876543210987654E16
+ </tnp05>
+ val diffs = XMLUtils.computeDiff(d1, d2, false).toList
+ assertTrue(diffs.isEmpty)
+ }
+
+ @Test def testDoubleDiffWithEpsilon(): Unit = {
+ val d1 = "9.09"
+ val d2 = "9.11"
+ val isSame = XMLUtils.textIsSame(d1, d2, Some("xs:double"), None,
Some(0.1))
+ assertTrue(isSame)
+ }
+
+ @Test def testDoubleDiffWithEpsilonNeg(): Unit = {
+ val d1 = "9.09"
+ val d2 = "9.11"
+ val isSame = XMLUtils.textIsSame(d1, d2, Some("xs:double"), None,
Some(0.01))
+ assertFalse(isSame)
+ }
+
+ @Test def testFloatDiffNoEpsilon(): Unit = {
+ val d1 = <tnp05 xmlns:xsi={xsiNS} xmlns:xs={xsNS}
xsi:type="xs:float">6.5400003E9</tnp05>
+ val d2 = <tnp05 xmlns:xsi={xsiNS} xmlns:xs={xsNS}
xsi:type="xs:float">6.54E9</tnp05>
+ val diffs = XMLUtils.computeDiff(d1, d2, false).toList
+ assertTrue(diffs.isEmpty)
+ }
+
+ @Test def testFloatDiffWithEpsilon(): Unit = {
+ val d1 = "9.09"
+ val d2 = "9.11"
+ val isSame = XMLUtils.textIsSame(d1, d2, Some("xs:float"),
Some(0.1.toFloat), None)
+ assertTrue(isSame)
+ }
+
+ @Test def testFloatDiffWithEpsilonNeg(): Unit = {
+ val d1 = "9.09"
+ val d2 = "9.11"
+ val isSame = XMLUtils.textIsSame(d1, d2, Some("xs:float"),
Some(0.01.toFloat), None)
+ assertFalse(isSame)
+ }
+
@Test def testIsNil(): Unit = {
val d1 = JDOMUtils.elem2Element(<a xmlns:xsi={XMLUtils.XSI_NAMESPACE}
xsi:nil="true"/>)
val d2 = JDOMUtils.elem2Element(<a
xmlns:xsi={XMLUtils.XSI_NAMESPACE}>foo</a>)
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala
index a844a063a..fe669c21a 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala
@@ -658,13 +658,8 @@ object NodeInfo extends Enum {
with PrimNumericFloat
with FloatView {
type Kind = FloatKind
- protected override def fromString(s: String) = {
- val f: JFloat = s match {
- case XMLUtils.PositiveInfinityString => JFloat.POSITIVE_INFINITY
- case XMLUtils.NegativeInfinityString => JFloat.NEGATIVE_INFINITY
- case XMLUtils.NaNString => JFloat.NaN
- case _ => s.toFloat
- }
+ protected override def fromString(s: String): DataValueFloat = {
+ val f: JFloat = XMLUtils.strToFloat(s)
f
}
protected override def fromNumberNoCheck(n: Number): DataValueFloat =
n.floatValue
@@ -681,12 +676,7 @@ object NodeInfo extends Enum {
with DoubleView {
type Kind = DoubleKind
protected override def fromString(s: String): DataValueDouble = {
- val d: JDouble = s match {
- case XMLUtils.PositiveInfinityString => JDouble.POSITIVE_INFINITY
- case XMLUtils.NegativeInfinityString => JDouble.NEGATIVE_INFINITY
- case XMLUtils.NaNString => JDouble.NaN
- case _ => s.toDouble
- }
+ val d: JDouble = XMLUtils.strToDouble(s)
d
}
protected override def fromNumberNoCheck(n: Number): DataValueDouble =
n.doubleValue
diff --git
a/daffodil-test-ibm1/src/test/resources/test-suite/tresys-contributed/BG.tdml
b/daffodil-test-ibm1/src/test/resources/test-suite/tresys-contributed/BG.tdml
index 256c1c29a..2b869d5c7 100644
---
a/daffodil-test-ibm1/src/test/resources/test-suite/tresys-contributed/BG.tdml
+++
b/daffodil-test-ibm1/src/test/resources/test-suite/tresys-contributed/BG.tdml
@@ -17,7 +17,8 @@
-->
<testSuite suiteName="BG" xmlns="http://www.ibm.com/xmlns/dfdl/testData"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ex="http://example.com"
defaultImplementations="ibm daffodil">
@@ -30,20 +31,20 @@
<infoset>
<dfdlInfoset>
<ex:list>
- <x>9.87654321001E9</x>
- <x>12345.6</x>
- <x>1.23456789123456784E17</x>
- <x>INF</x>
- <x>NaN</x>
- <x>0.0</x>
- <x>0.0</x>
- <x>0.0</x>
+ <x xsi:type="xs:double">9.87654321001E9</x>
+ <x xsi:type="xs:double">12345.6</x>
+ <x xsi:type="xs:double">1.23456789123456784E17</x>
+ <x xsi:type="xs:double">INF</x>
+ <x xsi:type="xs:double">NaN</x>
+ <x xsi:type="xs:double">0.0</x>
+ <x xsi:type="xs:double">0.0</x>
+ <x xsi:type="xs:double">0.0</x>
<y>187723572702975</y>
<y>986895</y>
<y>4886718345</y>
- <z>10.1</z>
- <z>20.3</z>
- <z>-9.12E-11</z>
+ <z xsi:type="xs:float">10.1</z>
+ <z xsi:type="xs:float">20.3</z>
+ <z xsi:type="xs:float">-9.12E-11</z>
</ex:list>
</dfdlInfoset>
</infoset>
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml
index e652f9812..6c49131d3 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml
@@ -3426,7 +3426,7 @@
</tdml:document>
<tdml:infoset>
<tdml:dfdlInfoset>
- <d_02>9.8765432109876544E16</d_02>
+ <d_02 xsi:type="xs:double">9.8765432109876544E16</d_02>
</tdml:dfdlInfoset>
</tdml:infoset>
</tdml:parserTestCase>
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section13/text_number_props/TextNumberProps.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section13/text_number_props/TextNumberProps.tdml
index abf3c3259..272d3f623 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section13/text_number_props/TextNumberProps.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section13/text_number_props/TextNumberProps.tdml
@@ -22,7 +22,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
- xmlns:ex="http://example.com"
+ xmlns:ex="http://example.com"
xmlns="http://example.com"
xmlns:tns="http://example.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
@@ -609,7 +609,7 @@
</tdml:document>
<tdml:infoset>
<tdml:dfdlInfoset>
- <tnp05>6.5400003E9</tnp05>
+ <tnp05 xsi:type="xs:float">6.5400003E9</tnp05>
</tdml:dfdlInfoset>
</tdml:infoset>