From 48b3f70c8d185afde42f37b2deb5e44ad4ee5525 Mon Sep 17 00:00:00 2001 From: Michael Beckerle Date: Sat, 23 Sep 2023 14:03:38 -0400 Subject: [PATCH] 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 ++-- .../scala/org/apache/daffodil/cli/Main.scala | 8 +- .../daffodil/core/api/TestForHeapDump.scala | 2 - .../daffodil/layers/TestJavaIOStreams.scala | 12 +- .../org/apache/daffodil/lib/util/Misc.scala | 118 ++++++++++----- .../apache/daffodil/lib/xml/XMLUtils.scala | 142 ++++++++++++++---- .../lib/xml/test/unit/TestXMLUtils.scala | 52 ++++++- .../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 9809bd5a5f..1c01d8b7f2 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 80409ac306..5ecc7c3d9c 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 e0ddf4f59d..c9868234b3 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 0b9a277b72..eba14b049a 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 11ef999d58..70103e6f08 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 0a1d88b615..176bbda561 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 c833b52b40..f74f672f82 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 = + 9.8765432109876544E16 + + val d2 = + 9.876543210987654E16 + + 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 = 6.5400003E9 + val d2 = 6.54E9 + 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() val d2 = JDOMUtils.elem2Element(foo) 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 a844a063af..fe669c21a1 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 256c1c29ac..2b869d5c77 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 @@ --> @@ -30,20 +31,20 @@ - 9.87654321001E9 - 12345.6 - 1.23456789123456784E17 - INF - NaN - 0.0 - 0.0 - 0.0 + 9.87654321001E9 + 12345.6 + 1.23456789123456784E17 + INF + NaN + 0.0 + 0.0 + 0.0 187723572702975 986895 4886718345 - 10.1 - 20.3 - -9.12E-11 + 10.1 + 20.3 + -9.12E-11 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 e652f98125..6c49131d30 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 @@ - 9.8765432109876544E16 + 9.8765432109876544E16 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 abf3c32599..272d3f6233 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 @@ - 6.5400003E9 + 6.5400003E9