stevedlawrence closed pull request #157: Add support for type aware checking in
the TDML runner
URL: https://github.com/apache/incubator-daffodil/pull/157
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
index bef2ea3ce..fee4c517e 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
@@ -17,29 +17,32 @@
package org.apache.daffodil.grammar.primitives
-import java.text.ParsePosition
import com.ibm.icu.text.SimpleDateFormat
import com.ibm.icu.util.Calendar
+import com.ibm.icu.util.Calendar
+import com.ibm.icu.util.TimeZone
import com.ibm.icu.util.TimeZone
import com.ibm.icu.util.ULocale
+
+import java.text.ParsePosition
+
+import scala.Boolean
+
+import org.apache.daffodil.calendar.TextCalendarConstants
import org.apache.daffodil.dsom.ElementBase
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.grammar.Terminal
-import org.apache.daffodil.schema.annotation.props.gen.CalendarCheckPolicy
-import org.apache.daffodil.schema.annotation.props.gen.CalendarFirstDayOfWeek
-import org.apache.daffodil.schema.annotation.props.gen.CalendarPatternKind
-import
org.apache.daffodil.processors.unparsers.ConvertBinaryCalendarSecMilliUnparser
-import org.apache.daffodil.processors.unparsers.ConvertTextCalendarUnparser
-import com.ibm.icu.util.Calendar
-import com.ibm.icu.util.TimeZone
import org.apache.daffodil.processors.CalendarEv
import org.apache.daffodil.processors.CalendarLanguageEv
import
org.apache.daffodil.processors.parsers.ConvertBinaryCalendarSecMilliParser
import org.apache.daffodil.processors.parsers.ConvertTextCalendarParser
-import org.apache.daffodil.processors.parsers.TextCalendarConstants
+import
org.apache.daffodil.processors.unparsers.ConvertBinaryCalendarSecMilliUnparser
+import org.apache.daffodil.processors.unparsers.ConvertTextCalendarUnparser
import org.apache.daffodil.schema.annotation.props.gen.BinaryCalendarRep
+import org.apache.daffodil.schema.annotation.props.gen.CalendarCheckPolicy
+import org.apache.daffodil.schema.annotation.props.gen.CalendarFirstDayOfWeek
+import org.apache.daffodil.schema.annotation.props.gen.CalendarPatternKind
import org.apache.daffodil.schema.annotation.props.gen.Representation
-import scala.Boolean
abstract class ConvertCalendarPrimBase(e: ElementBase, guard: Boolean)
extends Terminal(e, guard) {
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
index 11c47c32a..c7994d40d 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
@@ -50,15 +50,9 @@ object TestUtils {
/**
* Compares two XML Elements, after having (optionally) stripped off all
attributes.
- *
- * TODO: we might start using xsi:type attributes at some point. If so fix
this to
- * save that attribute.
- *
- * NOTE: Has Side Effects: strips off attributes
*/
- def assertEqualsXMLElements(expected: Node, actual: Node, stripAttributes:
Boolean = true) {
- val (exp, act) = if (!stripAttributes) (expected, actual) else
(XMLUtils.removeAttributes(expected), XMLUtils.removeAttributes(actual))
- XMLUtils.compareAndReport(Utility.trim(exp), Utility.trim(act))
+ def assertEqualsXMLElements(expected: Node, actual: Node) {
+ XMLUtils.compareAndReport(expected, actual)
}
/**
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
similarity index 66%
rename from
daffodil-runtime1/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
rename to
daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
index 6c5743eed..9af33158c 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
+++
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
@@ -18,13 +18,12 @@
package org.apache.daffodil.calendar
import com.ibm.icu.text.SimpleDateFormat
-import com.ibm.icu.util.Calendar;
-import com.ibm.icu.util.TimeZone;
+import com.ibm.icu.util.Calendar
+import com.ibm.icu.util.TimeZone
import java.math.{ BigDecimal => JBigDecimal }
import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.processors.parsers.TextCalendarConstants
object DFDLCalendarOrder extends Enumeration {
type DFDLCalendarOrder = Value
@@ -96,20 +95,26 @@ trait OrderedCalendar { self: DFDLCalendar =>
else if (pHasTZ && !qHasTZ) {
val qPlus = qPrime.getDateTimePlusFourteenHours
- if (orderCompareFields(pPrime, qPlus) ==
DFDLCalendarOrder.P_LESS_THAN_Q) { DFDLCalendarOrder.P_LESS_THAN_Q }
+ if (orderCompareFields(pPrime, qPlus) ==
DFDLCalendarOrder.P_LESS_THAN_Q)
+ DFDLCalendarOrder.P_LESS_THAN_Q
else {
val qMinus = q.getDateTimeMinusFourteenHours
- if (orderCompareFields(pPrime, qMinus) ==
DFDLCalendarOrder.P_GREATER_THAN_Q) { DFDLCalendarOrder.P_GREATER_THAN_Q }
- else { DFDLCalendarOrder.P_NOT_EQUAL_Q }
+ if (orderCompareFields(pPrime, qMinus) ==
DFDLCalendarOrder.P_GREATER_THAN_Q)
+ DFDLCalendarOrder.P_GREATER_THAN_Q
+ else
+ DFDLCalendarOrder.P_NOT_EQUAL_Q
}
} else if (!pHasTZ && qHasTZ) {
val pMinus = pPrime.getDateTimeMinusFourteenHours
- if (orderCompareFields(pMinus, qPrime) ==
DFDLCalendarOrder.P_LESS_THAN_Q) { DFDLCalendarOrder.P_LESS_THAN_Q }
+ if (orderCompareFields(pMinus, qPrime) ==
DFDLCalendarOrder.P_LESS_THAN_Q)
+ DFDLCalendarOrder.P_LESS_THAN_Q
else {
val pPlus = pPrime.getDateTimePlusFourteenHours
- if (orderCompareFields(pPlus, qPrime) ==
DFDLCalendarOrder.P_GREATER_THAN_Q) { DFDLCalendarOrder.P_GREATER_THAN_Q }
- else { DFDLCalendarOrder.P_NOT_EQUAL_Q }
+ if (orderCompareFields(pPlus, qPrime) ==
DFDLCalendarOrder.P_GREATER_THAN_Q)
+ DFDLCalendarOrder.P_GREATER_THAN_Q
+ else
+ DFDLCalendarOrder.P_NOT_EQUAL_Q
}
} else { Assert.impossibleCase }
}
@@ -133,14 +138,14 @@ trait OrderedCalendar { self: DFDLCalendar =>
for (i <- 0 until length) {
val field = fieldsForComparison(i)
- val hasPSubI = p.isFieldSet(field)
- val hasQSubI = q.isFieldSet(field)
+ val hasPSubI = p.calendar.isSet(field)
+ val hasQSubI = q.calendar.isSet(field)
if (!hasPSubI && !hasQSubI) { /* continue */ }
else if (hasPSubI ^ hasQSubI) return DFDLCalendarOrder.P_NOT_EQUAL_Q
else {
- val pSubI = p.getField(field)
- val qSubI = q.getField(field)
+ val pSubI = p.calendar.get(field)
+ val qSubI = q.calendar.get(field)
if (pSubI < qSubI) { return DFDLCalendarOrder.P_LESS_THAN_Q }
else if (pSubI > qSubI) { return DFDLCalendarOrder.P_GREATER_THAN_Q }
@@ -152,31 +157,23 @@ trait OrderedCalendar { self: DFDLCalendar =>
}
trait ToDateTimeMixin { self: DFDLCalendar =>
- def toDateTime(): DFDLDateTime = new
DFDLDateTime(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
+ def toDateTime(): DFDLDateTime =
DFDLDateTime(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
}
trait ToTimeMixin { self: DFDLCalendar =>
- def toTime(): DFDLTime = new
DFDLTime(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
+ def toTime(): DFDLTime = DFDLTime(calendar.clone().asInstanceOf[Calendar],
self.hasTimeZone)
}
trait ToDateMixin { self: DFDLCalendar =>
- def toDate(): DFDLDate = new
DFDLDate(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
+ def toDate(): DFDLDate = DFDLDate(calendar.clone().asInstanceOf[Calendar],
self.hasTimeZone)
}
-case class DFDLDate(calendar: Calendar, parsedTZ: Boolean)
- extends DFDLCalendar(parsedTZ: Boolean)
- with ToDateTimeMixin with ToDateMixin {
+case class DFDLDate(calendar: Calendar, override val hasTimeZone: Boolean)
+ extends DFDLCalendar
+ with ToDateTimeMixin
+ with ToDateMixin {
- /**
- * This apply method supplies a way to just specify 'no time zone' when
- * one was not expected. This just so happens to be the same as GMT.
- */
- def apply(cal: Calendar, expectsTZ: Boolean) = {
- if (!expectsTZ) cal.setTimeZone(TimeZone.UNKNOWN_ZONE)
- new DFDLDate(cal, expectsTZ)
- }
-
- @transient override lazy val tlFormatter = if (this.hasTimeZone)
TextCalendarConstants.tlDateInfosetFormatter else
TextCalendarConstants.tlDateNoTZInfosetFormatter
+ override def toString(): String = DFDLDateConversion.toXMLString(this)
override def equals(other: Any): Boolean = other match {
case that: DFDLDate => this.toDateTimeWithReference equals
that.toDateTimeWithReference
@@ -205,31 +202,16 @@ case class DFDLDate(calendar: Calendar, parsedTZ: Boolean)
dateCal.clear(Calendar.HOUR_OF_DAY)
dateCal.clear(Calendar.MINUTE)
dateCal.clear(Calendar.SECOND)
- new DFDLDateTime(dateCal, parsedTZ)
+ DFDLDateTime(dateCal, hasTimeZone)
}
}
-case class DFDLTime(calendar: Calendar, parsedTZ: Boolean)
- extends DFDLCalendar(parsedTZ)
+case class DFDLTime(calendar: Calendar, override val hasTimeZone: Boolean)
+ extends DFDLCalendar
with ToTimeMixin {
- /**
- * This apply method supplies a way to just specify 'no time zone' when
- * one was not expected. This just so happens to be the same as GMT.
- */
- def apply(cal: Calendar, expectsTZ: Boolean) = {
- if (!expectsTZ) cal.setTimeZone(TimeZone.UNKNOWN_ZONE)
- new DFDLTime(cal, expectsTZ)
- }
-
- @transient override lazy val tlFormatter = (this.hasTimeZone,
this.hasFractionalSeconds) match {
- case (true, true) => TextCalendarConstants.tlTimeInfosetFormatter
- case (false, true) => TextCalendarConstants.tlTimeNoTZInfosetFormatter
- case (true, false) =>
TextCalendarConstants.tlTimeNoFractSecInfosetFormatter
- case (false, false) =>
TextCalendarConstants.tlTimeNoTZNoFractSecInfosetFormatter
- }
-
+ override def toString(): String = DFDLTimeConversion.toXMLString(this)
override def equals(other: Any): Boolean = other match {
case that: DFDLTime => this.toDateTimeWithReference equals
that.toDateTimeWithReference
@@ -261,30 +243,18 @@ case class DFDLTime(calendar: Calendar, parsedTZ: Boolean)
timeCal.clear(Calendar.EXTENDED_YEAR)
timeCal.clear(Calendar.MONTH)
timeCal.clear(Calendar.DAY_OF_MONTH)
- new DFDLDateTime(timeCal, parsedTZ)
+ DFDLDateTime(timeCal, hasTimeZone)
}
}
-case class DFDLDateTime(calendar: Calendar, parsedTZ: Boolean)
- extends DFDLCalendar(parsedTZ)
- with ToDateTimeMixin with ToDateMixin with ToTimeMixin {
+case class DFDLDateTime(calendar: Calendar, override val hasTimeZone: Boolean)
+ extends DFDLCalendar
+ with ToDateTimeMixin
+ with ToDateMixin
+ with ToTimeMixin {
- /**
- * This apply method supplies a way to just specify 'no time zone' when
- * one was not expected. This just so happens to be the same as GMT.
- */
- def apply(cal: Calendar, expectsTZ: Boolean) = {
- if (!expectsTZ) cal.setTimeZone(TimeZone.UNKNOWN_ZONE)
- new DFDLDateTime(cal, expectsTZ)
- }
-
- @transient override lazy val tlFormatter = (this.hasTimeZone,
this.hasFractionalSeconds) match {
- case (true, true) => TextCalendarConstants.tlDateTimeInfosetFormatter
- case (false, true) => TextCalendarConstants.tlDateTimeNoTZInfosetFormatter
- case (true, false) =>
TextCalendarConstants.tlDateTimeNoFractSecInfosetFormatter
- case (false, false) =>
TextCalendarConstants.tlDateTimeNoTZNoFractSecInfosetFormatter
- }
+ override def toString(): String = DFDLDateTimeConversion.toXMLString(this)
override def equals(other: Any) = other match {
case that: DFDLDateTime => dateTimeEqual(this, that)
@@ -303,14 +273,14 @@ case class DFDLDateTime(calendar: Calendar, parsedTZ:
Boolean)
def getDateTimePlusFourteenHours: DFDLDateTime = {
Assert.invariant(!hasTimeZone)
val adjustedCal = adjustTimeZone(normalizedCalendar, 14, 0)
- val dt = new DFDLDateTime(adjustedCal, parsedTZ)
+ val dt = DFDLDateTime(adjustedCal, hasTimeZone)
dt
}
def getDateTimeMinusFourteenHours: DFDLDateTime = {
Assert.invariant(!hasTimeZone)
val adjustedCal = adjustTimeZone(normalizedCalendar, -14, 0)
- val dt = new DFDLDateTime(adjustedCal, parsedTZ)
+ val dt = DFDLDateTime(adjustedCal, hasTimeZone)
dt
}
@@ -349,7 +319,7 @@ case class DFDLDateTime(calendar: Calendar, parsedTZ:
Boolean)
}
def getNormalizedCalendar(): DFDLDateTime = {
- new DFDLDateTime(normalizedCalendar.clone().asInstanceOf[Calendar],
parsedTZ)
+ DFDLDateTime(normalizedCalendar.clone().asInstanceOf[Calendar],
hasTimeZone)
}
/**
@@ -400,77 +370,33 @@ case class DFDLDateTime(calendar: Calendar, parsedTZ:
Boolean)
}
}
-abstract class DFDLCalendar(containsTZ: Boolean)
+/**
+ * Abstract class for DFDL Date/Time/DateTime. Wrapper around ICU calendar.
+ *
+ * The main reason this is neeed is there does not appear to really be a
+ * concept of 'no time zone', so a hasTimeZone function must be implemented by
+ * implementors specifying whether or not the calendar has a timezone.
+ *
+ * On a related note, it does appear that we can specify an 'unknown' time zone
+ * using TimeZone.UNKNOWN_ZONE, since this behaves like the GMT/UTC time zone.
+ *
+ * To be clear, when no time zone is present (or expected) we set the calendar
+ * time zone to TimeZone.UNKNOWN_ZONE and carry around the hasTimeZOne flag.
+ * This avoids accessing calendar.getZone and comparing against
+ * TimeZone.UNKNOWN_ZONE, but allows the calendar object to somewhat represent
+ * 'no time zone'.
+ */
+abstract class DFDLCalendar
extends OrderedCalendar with Serializable {
- /* Print formats */
- final val dateTimeFormatNoTZ: String = "uuuu-MM-dd'T'HH:mm:ss.SSSSSS"
- final val dateFormatNoTZ: String = "uuuu-MM-dd"
- final val timeFormatNoTZ: String = "HH:mm:ss.SSSSSS"
- final val dateTimeFormat: String = "uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx"
- final val dateFormat: String = "uuuu-MM-ddxxxxx"
- final val timeFormat: String = "HH:mm:ss.SSSSSSxxxxx"
- final val tzFormat: String = "xxx" // -08:00 The ISO8601 extended format
with hours and minutes fields.
+ // Ensure the calendar does not have a timezone set if the hasTimeZone flag
+ // has not been set
+ if (!hasTimeZone) calendar.setTimeZone(TimeZone.UNKNOWN_ZONE)
def calendar: Calendar
- def tlFormatter: ThreadLocal[SimpleDateFormat]
-
- def getField(fieldIndex: Int): Int = calendar.get(fieldIndex)
- def isFieldSet(fieldIndex: Int): Boolean = calendar.isSet(fieldIndex)
- def hasTimeZone: Boolean = {
- // There does not appear to really be a concept of 'no time zone'
- // so we use a regex or other means of elimination to determine
- // if a time zone was expected or parsed. This is passed as a flag
- // to the DFDLCalendar object.
- //
- // On a side note, it does appear that we can specify an 'unknown'
- // time zone using TimeZone.UNKNOWN_ZONE. However, this behaves like
- // the GMT/UTC time zone.
- //
- // To be clear, when no time zone is present (or expected) we set
- // the calendar time zone to TimeZone.UNKNOWN_ZONE and carry around
- // this flag. This avoids accessing calendar.getZone and comparing
- // against TimeZone.UNKNOWN_ZONE, but allows the calendar object
- // to somewhat represent 'no time zone'.
- //
- containsTZ
- }
- def hasFractionalSeconds: Boolean = (calendar.isSet(Calendar.MILLISECOND) &&
(calendar.get(Calendar.MILLISECOND) != 0))
+ def hasTimeZone: Boolean
final def toJBigDecimal: JBigDecimal = new
JBigDecimal(calendar.getTimeInMillis())
-
- /**
- * Returns the TimeZone in the format of "+00:00"
- */
- def getTimeZoneString: String = {
- val formatter = TextCalendarConstants.tlTzInfosetFormatter.get
- val formattedString = try {
- formatter.format(calendar)
- } catch {
- case ex: java.lang.IllegalArgumentException =>
- throw new java.lang.IllegalArgumentException("Calendar content failed
to match the format '%s' due to %s".format(formatter.toPattern,
ex.getMessage()))
- }
- formattedString
- }
-
- /**
- * Returns the ICU Calendar object.
- *
- * We represent 'No time zone' by TimeZone.UNKNOWN_ZONE
- */
- def getCalendar() = calendar
- override def toString(): String = {
- val formatter: SimpleDateFormat = tlFormatter.get
- formatter.setCalendar(calendar)
-
- val formattedString = try {
- formatter.format(calendar)
- } catch {
- case ex: java.lang.IllegalArgumentException =>
- throw new java.lang.IllegalArgumentException("Calendar content failed
to match the format '%s' due to %s".format(formatter.toPattern,
ex.getMessage()))
- }
- formattedString
- }
-
}
+
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendarConversion.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendarConversion.scala
new file mode 100644
index 000000000..0c4ecda5b
--- /dev/null
+++
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendarConversion.scala
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.calendar
+
+import com.ibm.icu.util.Calendar
+import com.ibm.icu.text.SimpleDateFormat
+import com.ibm.icu.util.TimeZone
+
+import java.text.ParseException
+import java.text.ParsePosition
+
+/**
+ * Wrapper arounda SimpleDateFormat and a flag that determines if it expects to
+ * parse a timezone or not
+ */
+case class DFDLCalendarFormat private (format: SimpleDateFormat,
expectsTimezone: Boolean)
+
+object DFDLCalendarFormat {
+ def apply(pattern: String, expectsTimezone: Boolean): DFDLCalendarFormat = {
+ new DFDLCalendarFormat(InfosetSimpleDateFormat(pattern), expectsTimezone)
+ }
+}
+
+object InfosetSimpleDateFormat {
+ def apply(pattern: String): SimpleDateFormat = {
+ val sdf = new SimpleDateFormat(pattern)
+ sdf.setLenient(false)
+ sdf
+ }
+}
+
+object DFDLCalendarConversion {
+
+ @inline
+ private def pad2(i: Int) = {
+ val istr = i.toString
+ if (i >= 10) istr
+ else "0" + istr
+ }
+
+ @inline
+ private def pad3(i: Int) = {
+ val istr = i.toString
+ if (i >= 100) istr
+ else if (i >= 10) "0" + istr
+ else "00" + istr
+ }
+
+ @inline
+ private def pad4(i: Int) = {
+ val istr = i.toString
+ if (i >= 1000) istr
+ else if (i >= 100) "0" + istr
+ else if (i >= 10) "00" + istr
+ else "000" + istr
+ }
+
+ def datePartToXMLString(dfdlcal: DFDLCalendar): String = {
+ val calendar = dfdlcal.calendar
+ val y = calendar.get(Calendar.EXTENDED_YEAR)
+ val m = calendar.get(Calendar.MONTH) + 1
+ val d = calendar.get(Calendar.DAY_OF_MONTH)
+
+ val ysign = if (y >= 0) "" else "-"
+
+ ysign + pad4(Math.abs(y)) + "-" + pad2(m) + "-" + pad2(d)
+ }
+
+ def timePartToXMLString(dfdlcal: DFDLCalendar): String = {
+ val calendar = dfdlcal.calendar
+ val h = calendar.get(Calendar.HOUR_OF_DAY)
+ val m = calendar.get(Calendar.MINUTE)
+ val s = calendar.get(Calendar.SECOND)
+ val u = calendar.get(Calendar.MILLISECOND)
+
+ pad2(h) + ":" + pad2(m) + ":" + pad2(s) + (if (u != 0) "." + pad3(u) +
"000" else "")
+ }
+
+ def timeZonePartToXMLString(dfdlcal: DFDLCalendar): String = {
+ if (!dfdlcal.hasTimeZone) {
+ ""
+ } else {
+ // This will return +00:00 for GMT. We may want to consider returning 'Z'
+ // instead when offsetInMils is zero. The DFDL specification is unclear
+ // on whether +00:00 or Z is expected. Note that the specification is
+ // clear that the dfdl:timeZoneFrom* functions (which are implemented
+ // using this function) should return +00:00 and not Z So if this
+ // function is changed to return a 'Z', it must be made configurable.
+ val tz = dfdlcal.calendar.getTimeZone
+ val offsetInMils = tz.getRawOffset
+ val offsetInMins = Math.abs(offsetInMils / (1000 * 60))
+ val h = offsetInMins / 60
+ val m = offsetInMins % 60
+ val s = if (offsetInMils >= 0) "+" else "-"
+
+ s + pad2(h) + ":" + pad2(m)
+ }
+ }
+}
+
+trait DFDLCalendarConversion {
+
+ val calendarType: String
+
+ protected def fromXMLFormats = new ThreadLocal[Seq[DFDLCalendarFormat]]
+
+ /**
+ * Attempts to parse a given date/time string with multiple allowable
+ * formats. If the string is completely parsed with one of the formats, it
+ * returns the resulting Calendar of the parse and the DFDLCalendarFormat
+ * that led to the successful match.
+ */
+ protected def parseFromXMLString(string: String): (Calendar,
DFDLCalendarFormat) = {
+ // Create strict calendar, initialize with no time zone to ensure timezone
+ // information only comes from the data if it exists
+ val calendar = Calendar.getInstance(TimeZone.UNKNOWN_ZONE)
+ calendar.setLenient(false)
+
+ val successfulFormat = fromXMLFormats.get.find { calendarFormat =>
+
+ val pos = new ParsePosition(0)
+ calendar.clear()
+ calendarFormat.format.parse(string, calendar, pos)
+
+ try {
+ calendar.getTime()
+
+ if (pos.getIndex() == 0 || pos.getErrorIndex() != -1 || pos.getIndex()
!= string.length()) {
+ false
+ } else {
+ true
+ }
+ } catch {
+ case _: IllegalArgumentException => {
+ // thrown by getTime() when parsed data is not strictly correct
+ false
+ }
+ }
+ }
+
+ if (successfulFormat.isEmpty) {
+ throw new IllegalArgumentException(
+ """Failed to parse "%s" to an %s""".format(string, calendarType))
+ }
+
+ (calendar, successfulFormat.get)
+ }
+}
+
+object DFDLDateTimeConversion extends DFDLCalendarConversion {
+
+ val calendarType = "xs:dateTime"
+
+ @transient
+ override protected lazy val fromXMLFormats = new
ThreadLocal[Seq[DFDLCalendarFormat]] {
+ override def initialValue = {
+ Seq(
+ DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx", true),
+ DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSS", false),
+ DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ssxxxxx", true),
+ DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ss", false),
+ DFDLCalendarFormat("uuuu-MM-ddxxxxx", true),
+ DFDLCalendarFormat("uuuu-MM-dd", false)
+ )
+ }
+ }
+
+ def fromXMLString(string: String): DFDLDateTime = {
+ val (calendar, format) = parseFromXMLString(string)
+ DFDLDateTime(calendar, format.expectsTimezone)
+ }
+
+ def toXMLString(dt: DFDLDateTime): String = {
+ DFDLCalendarConversion.datePartToXMLString(dt) +
+ "T" +
+ DFDLCalendarConversion.timePartToXMLString(dt) +
+ DFDLCalendarConversion.timeZonePartToXMLString(dt)
+ }
+}
+
+object DFDLDateConversion extends DFDLCalendarConversion {
+
+ val calendarType = "xs:date"
+
+ @transient
+ override protected lazy val fromXMLFormats = new
ThreadLocal[Seq[DFDLCalendarFormat]] {
+ override def initialValue = {
+ Seq(
+ DFDLCalendarFormat("uuuu-MM-ddxxxxx", true),
+ DFDLCalendarFormat("uuuu-MM-dd", false)
+ )
+ }
+ }
+
+ def fromXMLString(string: String): DFDLDate = {
+ val (calendar, format) = parseFromXMLString(string)
+ DFDLDate(calendar, format.expectsTimezone)
+ }
+
+ def toXMLString(d: DFDLDate): String = {
+ DFDLCalendarConversion.datePartToXMLString(d) +
+ DFDLCalendarConversion.timeZonePartToXMLString(d)
+ }
+}
+
+object DFDLTimeConversion extends DFDLCalendarConversion {
+
+ val calendarType = "xs:time"
+
+ @transient
+ override protected lazy val fromXMLFormats = new
ThreadLocal[Seq[DFDLCalendarFormat]] {
+ override def initialValue = {
+ Seq(
+ DFDLCalendarFormat("HH:mm:ss.SSSSSSxxxxx", true),
+ DFDLCalendarFormat("HH:mm:ss.SSSSSS", false),
+ DFDLCalendarFormat("HH:mm:ssxxxxx", true),
+ DFDLCalendarFormat("HH:mm:ss", false)
+ )
+ }
+ }
+
+ def fromXMLString(string: String): DFDLTime = {
+ val (calendar, format) = parseFromXMLString(string)
+ DFDLTime(calendar, format.expectsTimezone)
+ }
+
+
+ def toXMLString(t: DFDLTime): String = {
+ DFDLCalendarConversion.timePartToXMLString(t) +
+ DFDLCalendarConversion.timeZonePartToXMLString(t)
+ }
+}
diff --git
a/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/TextCalendarConstants.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/TextCalendarConstants.scala
new file mode 100644
index 000000000..bdd88fd4f
--- /dev/null
+++
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/TextCalendarConstants.scala
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.daffodil.calendar
+
+object TextCalendarConstants {
+ final val maxFractionalSeconds = 9
+}
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
index 2f6df383e..1c43cd65a 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
@@ -18,11 +18,18 @@
package org.apache.daffodil.xml
import java.io.File
-import scala.xml._
-import org.apache.daffodil.exceptions._
+
import scala.collection.mutable.ArrayBuilder
-import org.apache.commons.io.IOUtils
import scala.xml.NamespaceBinding
+import scala.xml._
+
+import org.apache.commons.io.IOUtils
+
+import org.apache.daffodil.exceptions._
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.calendar.DFDLDateConversion
+import org.apache.daffodil.calendar.DFDLDateTimeConversion
+import org.apache.daffodil.calendar.DFDLTimeConversion
import org.apache.daffodil.schema.annotation.props.LookupLocation
/**
@@ -209,6 +216,17 @@ object XMLUtils {
remapXMLCharacters(dfdlString, remapPUAToXMLIllegalChar(false))
}
+ def coalesceAllAdjacentTextNodes(node: Node): Node = {
+ node match {
+ case Elem(prefix, label, attribs, scope, child @ _*) => {
+ val coalescedChildren = child.map(coalesceAllAdjacentTextNodes(_))
+ val newChildren = coalesceAdjacentTextNodes(coalescedChildren)
+ Elem(prefix, label, attribs, scope, true, newChildren: _*)
+ }
+ case x => x
+ }
+ }
+
/*
* This is needed for equality comparison of XML.
*
@@ -224,15 +242,7 @@ object XMLUtils {
if (seq.length == 0) return seq
if (seq.length == 1) {
seq(0) match {
-
- case p: PCData => return seq
-
- case Text(data) =>
- if (data.matches("""\s*""")) return Nil
- else return seq
-
- case u: Unparsed => return seq // TODO: are these needed or possible?
-
+ case a: Atom[_] => return seq
case _ => // fall through to code below. (We need to process children)
}
}
@@ -616,10 +626,8 @@ object XMLUtils {
def convertPCDataToText(n: Node): Node = {
val res = n match {
- case PCData(data) => {
- val t = Text(n.text)
- t
- }
+ case t: Text => t
+ case a: Atom[_] => Text(a.text)
case Elem(prefix, label, attributes, scope, children @ _*) => {
val newChildren = children.map { convertPCDataToText(_) }
Elem(prefix, label, attributes, scope, true, newChildren: _*)
@@ -716,14 +724,25 @@ object XMLUtils {
res
}
- def compareAndReport(trimmedExpected: Node, actualNoAttrs: Node,
ignoreProcInstr: Boolean = true) = {
- if (trimmedExpected != actualNoAttrs) {
- val expString = trimmedExpected.toString
- val actString = actualNoAttrs.toString
- if (expString != actString) {
- val diffs = XMLUtils.computeDiff(trimmedExpected, actualNoAttrs,
ignoreProcInstr)
- if (diffs.length > 0) {
- throw new Exception("""
+ /**
+ * Prepares an XML node for diff comparison
+ **/
+ private def prepareForDiffComparison(n: Node): Node = {
+ val noComments = removeComments(n)
+ val noPCData = convertPCDataToText(noComments)
+ val combinedText = coalesceAllAdjacentTextNodes(noPCData)
+ val noMixedWS = removeMixedWhitespace(combinedText)
+ noMixedWS
+ }
+
+ class XMLDifferenceException(message: String) extends Exception(message)
+
+ def compareAndReport(expected: Node, actual: Node, ignoreProcInstr: Boolean
= true) = {
+ val expectedMinimized = prepareForDiffComparison(expected)
+ val actualMinimized = prepareForDiffComparison(actual)
+ val diffs = XMLUtils.computeDiff(expectedMinimized, actualMinimized,
ignoreProcInstr)
+ if (diffs.length > 0) {
+ throw new XMLDifferenceException("""
Comparison failed.
Expected
%s
@@ -731,9 +750,9 @@ Actual
%s
Differences were (path, expected, actual):
%s""".format(
- trimmedExpected.toString, actualNoAttrs.toString, diffs.map {
_.toString }.mkString("\n")))
- }
- }
+ removeAttributes(expected).toString,
+ removeAttributes(actual).toString,
+ diffs.map { _.toString }.mkString("", "\n", "\n")))
}
}
@@ -742,7 +761,7 @@ Differences were (path, expected, actual):
* Each triple is the path (an x-path-like string), followed by expected,
and actual values.
*/
def computeDiff(a: Node, b: Node, ignoreProcInstr: Boolean = true) = {
- computeDiffOne(Seq(a), Seq(b), Map.empty, Nil, ignoreProcInstr)
+ computeDiffOne(Seq(a), Seq(b), Map.empty, Nil, ignoreProcInstr, None)
}
def childArrayCounters(e: Elem) = {
@@ -755,27 +774,38 @@ Differences were (path, expected, actual):
arrayCounters
}
- def computeDiffOne(as: Seq[Node], bs: Seq[Node],
+ def computeDiffOne(
+ as: Seq[Node],
+ bs: Seq[Node],
aCounters: Map[String, Long],
path: Seq[String],
- ignoreProcInstr: Boolean = true): Seq[(String, String, String)] = {
+ ignoreProcInstr: Boolean,
+ maybeType: Option[String]): Seq[(String, String, String)] = {
lazy val zPath = path.reverse.mkString("/")
(as, bs) match {
case (a1 :: ars, b1 :: brs) if (a1.isInstanceOf[Elem] &&
b1.isInstanceOf[Elem]) => {
val (a: Elem, b: Elem) = (a1, b1)
val Elem(_, labelA, attribsA, _, childrenA @ _*) = a
val Elem(_, labelB, attribsB, _, childrenB @ _*) = b
- if (labelA != labelB)
- List((zPath, a.toString, b.toString))
- else if (attribsA != attribsB
- && !((attribsA == null && (attribsB == null || attribsB.length == 0))
- || (attribsB == null) && attribsA.length == 0)) {
-
- // println("attributes are different")
-
- val aA = if (attribsA == null || attribsA == "") "null" else
attribsA.toString
- val aB = if (attribsB == null || attribsB == "") "null" else
attribsB.toString
- List((zPath, aA, aB))
+ 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 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")
+
+ if (labelA != labelB) {
+ // different label
+ List((zPath, labelA, labelB))
+ } else if (nilledA != nilledB) {
+ // different xsi:nil
+ List((zPath + "/" + labelA + "@xsi:nil",
+ nilledA.map(_.toString).getOrElse(""),
+ nilledB.map(_.toString).getOrElse("")))
+ } else if (typeA != typeB && typeA.isDefined && typeB.isDefined) {
+ // different xsi:type (if both suppplied)
+ List((zPath + "/" + labelA + "@xsi:type",
+ typeA.map(_.toString).getOrElse(""),
+ typeA.map(_.toString).getOrElse("")))
} else {
val aIndex = aCounters.get(labelA)
val aIndexExpr = aIndex.map { n => labelA + "[" + n + "]" }
@@ -790,27 +820,28 @@ Differences were (path, expected, actual):
val newPath = pathStep +: path
val childrenAList = childrenA.toList
val childrenBList = childrenB.toList
+
val childrenDiffs =
- computeDiffOne(childrenAList, childrenBList, aChildArrayCounters,
newPath, ignoreProcInstr)
- val subsequentPeerDiffs = computeDiffOne(ars, brs, newACounters,
path, ignoreProcInstr)
+ computeDiffOne(childrenAList, childrenBList, aChildArrayCounters,
newPath, ignoreProcInstr, maybeType)
+ val subsequentPeerDiffs = computeDiffOne(ars, brs, newACounters,
path, ignoreProcInstr, maybeType)
val res = childrenDiffs ++ subsequentPeerDiffs
res
}
}
case (tA1 :: ars, tB1 :: brs) if (tA1.isInstanceOf[Text] &&
tB1.isInstanceOf[Text]) => {
val (tA: Text, tB: Text) = (tA1, tB1)
- val thisDiff = computeTextDiff(zPath, tA, tB)
- val restDiffs = computeDiffOne(ars, brs, aCounters, path,
ignoreProcInstr)
+ val thisDiff = computeTextDiff(zPath, tA, tB, maybeType)
+ val restDiffs = computeDiffOne(ars, brs, aCounters, path,
ignoreProcInstr, maybeType)
val res = thisDiff ++ restDiffs
res
}
case (tA1 :: ars, brs) if (ignoreProcInstr &&
tA1.isInstanceOf[scala.xml.ProcInstr]) =>
- computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr)
+ computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr, maybeType)
case (ars, tB1 :: brs) if (ignoreProcInstr &&
tB1.isInstanceOf[scala.xml.ProcInstr]) =>
- computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr)
+ computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr, maybeType)
case (scala.xml.ProcInstr(tA1label, tA1content) :: ars,
scala.xml.ProcInstr(tB1label, tB1content) :: brs) => {
- val labelDiff = computeTextDiff(zPath, tA1label, tB1label)
+ val labelDiff = computeTextDiff(zPath, tA1label, tB1label, 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
@@ -822,8 +853,8 @@ Differences were (path, expected, actual):
//
// TODO: implement XML-comparison for our data format info PIs.
//
- val contentDiff = computeTextDiff(zPath, tA1content, tB1content)
- val restDiffs = computeDiffOne(ars, brs, aCounters, path,
ignoreProcInstr)
+ val contentDiff = computeTextDiff(zPath, tA1content, tB1content,
maybeType)
+ val restDiffs = computeDiffOne(ars, brs, aCounters, path,
ignoreProcInstr, maybeType)
val res = labelDiff ++ contentDiff ++ restDiffs
res
}
@@ -845,15 +876,26 @@ Differences were (path, expected, actual):
}
}
- def computeTextDiff(zPath: String, tA: Text, tB: Text): Seq[(String, String,
String)] = {
+ def computeTextDiff(
+ zPath: String,
+ tA: Text,
+ tB:Text,
+ maybeType: Option[String]): Seq[(String, String, String)] = {
+
val dataA = tA.toString
val dataB = tB.toString
- computeTextDiff(zPath, dataA, dataB)
+ computeTextDiff(zPath, dataA, dataB, maybeType)
}
- def computeTextDiff(zPath: String, dataA: String, dataB: String):
Seq[(String, String, String)] = {
+ def computeTextDiff(
+ zPath: String,
+ dataA: String,
+ dataB: String,
+ maybeType: Option[String]): Seq[(String, String, String)] = {
+
def quoteIt(str: String) = "'" + str + "'"
- if (dataA == dataB) Nil
+
+ if (textIsSame(dataA, dataB, maybeType)) Nil
else if (dataA.length != dataB.length) {
List((zPath, quoteIt(dataA), quoteIt(dataB)))
} else {
@@ -871,6 +913,28 @@ Differences were (path, expected, actual):
}
}
+ def textIsSame(dataA: String, dataB: String, maybeType: Option[String]):
Boolean = {
+ maybeType match {
+ case Some("xs:hexBinary") => dataA.equalsIgnoreCase(dataB)
+ case Some("xs:date") => {
+ val a = DFDLDateConversion.fromXMLString(dataA)
+ val b = DFDLDateConversion.fromXMLString(dataB)
+ a == b
+ }
+ case Some("xs:time") => {
+ val a = DFDLTimeConversion.fromXMLString(dataA)
+ val b = DFDLTimeConversion.fromXMLString(dataB)
+ a == b
+ }
+ case Some("xs:dateTime") => {
+ val a = DFDLDateTimeConversion.fromXMLString(dataA)
+ val b = DFDLDateTimeConversion.fromXMLString(dataB)
+ a == b
+ }
+ case _ => dataA == dataB
+ }
+ }
+
/**
* for quick tests, we use literal scala nodes. However, the underlying
* infrastructure wants to be all file centric for diagnostic-message
diff --git
a/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
b/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
index dc9f04e80..a7568c33b 100644
---
a/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
+++
b/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
@@ -30,7 +30,7 @@ class TestXMLUtils {
@Test def testDiff0() {
val d1 = new Text("a")
val d2 = new Text("b")
- val diffs = XMLUtils.computeTextDiff("", d1, d2)
+ val diffs = XMLUtils.computeTextDiff("", d1, d2, None)
val Seq((p, a, b)) = diffs
assertEquals(".charAt(1)", p)
assertEquals("a(%#x0061;)", a)
@@ -74,14 +74,12 @@ class TestXMLUtils {
@Test def testNilDiff1() {
val d1 = <a xmlns:xsi={ XMLUtils.XSI_NAMESPACE } xsi:nil="true" foo="bar"/>
val d2 = <a dafint:col="30" xmlns:dafint={ XMLUtils.INT_NS }/>
- val d1NoA = XMLUtils.removeAttributes(d1)
- val d2NoA = XMLUtils.removeAttributes(d2)
- val diffs = XMLUtils.computeDiff(d1NoA, d2NoA)
+ val diffs = XMLUtils.computeDiff(d1, d2)
val Seq((path, d1attribs, d2attribs)) = diffs
- assertEquals("", path)
+ assertEquals("/a@xsi:nil", path)
// for whatever reason, an attribute prints with a leading space or
something.
// so we make the comparison be a 'contains' style comparison.
- assertTrue(d1attribs.contains("xsi:nil=\"true\""))
+ assertTrue(d1attribs.contains("true"))
assertEquals("", d2attribs)
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala
index 85c75dfcb..0e981368f 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala
@@ -18,13 +18,6 @@
package org.apache.daffodil.dpath
import org.apache.daffodil.exceptions.ThrowsSDE
-import java.text.ParsePosition
-import com.ibm.icu.util.Calendar
-import com.ibm.icu.text.SimpleDateFormat
-import java.text.ParseException
-import org.apache.daffodil.calendar.DFDLDateTime
-import org.apache.daffodil.calendar.DFDLTime
-import org.apache.daffodil.calendar.DFDLDate
/*
* Casting chart taken from http://www.w3.org/TR/xpath-functions/#casting, with
@@ -294,61 +287,4 @@ object Conversion {
ops
}
- protected def constructCalendar(value: String, inFormat: SimpleDateFormat,
fncName: String, toType: String): Calendar = {
- val isLenient = inFormat.isLenient()
-
- try {
- inFormat.setLenient(false)
- inFormat.parse(value)
- true
- } catch {
- case e: ParseException => {
- inFormat.setLenient(isLenient)
- throw new java.lang.IllegalArgumentException(String.format("Conversion
Error: %s failed to convert \"%s\" to %s. Due to %s",
- fncName, value.toString, toType, "Failed to match format."))
- }
- }
-
- val calendar = inFormat.getCalendar()
- val pos = new ParsePosition(0)
-
- inFormat.parse(value.toString, calendar, pos)
-
- try {
- calendar.getTime()
- } catch {
- case ex: java.lang.IllegalArgumentException => {
- inFormat.setLenient(isLenient)
- throw new java.lang.IllegalArgumentException(String.format("Conversion
Error: %s failed to convert \"%s\" to %s. Due to %s",
- fncName, value.toString, toType, ex.getMessage()))
- }
- }
-
- inFormat.setLenient(isLenient)
-
- if (pos.getIndex() == 0 || pos.getErrorIndex() != -1) {
- throw new java.lang.IllegalArgumentException(String.format("Conversion
Error: %s failed to convert \"%s\" to %s due to a parse error.",
- fncName, value.toString, toType))
- }
- if (pos.getIndex() != (value.length())) {
- throw new java.lang.IllegalArgumentException(String.format("Conversion
Error: %s failed to convert \"%s\" to %s. Failed to use up all characters in
the parse. Stopped at %s.",
- fncName, value.toString, toType, pos.getIndex().toString))
- }
- calendar
- }
-
- def stringToDFDLDateTime(value: String, inFormat: SimpleDateFormat,
expectsTZ: Boolean, fncName: String, toType: String): DFDLDateTime = {
- val calendar = constructCalendar(value, inFormat, fncName, toType)
- DFDLDateTime(calendar, expectsTZ)
- }
-
- def stringToDFDLDate(value: String, inFormat: SimpleDateFormat, expectsTZ:
Boolean, fncName: String, toType: String): DFDLDate = {
- val calendar = constructCalendar(value, inFormat, fncName, toType)
- DFDLDate(calendar, expectsTZ)
- }
-
- def stringToDFDLTime(value: String, inFormat: SimpleDateFormat, expectsTZ:
Boolean, fncName: String, toType: String): DFDLTime = {
- val calendar = constructCalendar(value, inFormat, fncName, toType)
- DFDLTime(calendar, expectsTZ)
- }
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
index f38ce73cf..1b43e1eb6 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
@@ -19,10 +19,14 @@ package org.apache.daffodil.dpath
import com.ibm.icu.text.SimpleDateFormat
import com.ibm.icu.util.Calendar
-import org.apache.daffodil.calendar.DFDLDateTime
+
import org.apache.daffodil.calendar.DFDLCalendar
-import org.apache.daffodil.calendar.DFDLTime
import org.apache.daffodil.calendar.DFDLDate
+import org.apache.daffodil.calendar.DFDLDateConversion
+import org.apache.daffodil.calendar.DFDLDateTime
+import org.apache.daffodil.calendar.DFDLDateTimeConversion
+import org.apache.daffodil.calendar.DFDLTime
+import org.apache.daffodil.calendar.DFDLTimeConversion
case object AnyAtomicToString extends Converter {
override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
@@ -33,128 +37,44 @@ case object AnyAtomicToString extends Converter {
}
}
-trait XSDateTimeKind extends Serializable {
- val timeZoneID = "UTC"
- @transient lazy val calendar = new ThreadLocal[Calendar] {
- override def initialValue = {
- val cal = Calendar.getInstance()
- cal.clear()
- cal.setLenient(false)
- //
- // We don't want to set a TimeZone here. It should be 'parsed' if it's
- // there. If it's not, the TimeZone will be set to TimeZone.UNKNOWN_ZONE
- // which will operate just like GMT/UTC
- //
- cal
- }
- }
-
- def defaultFormatter: ThreadLocal[DFDLDateFormat]
- def acceptableFormats: Seq[DFDLDateFormat]
-
- def getNewCalendar: Calendar = calendar.get.clone().asInstanceOf[Calendar]
-
- protected def createCalendar(str: String, inFormat: SimpleDateFormat,
expectsTZ: Boolean,
- fncName: String, toType: String): DFDLCalendar
-
- def matchFormat(str: String, fncName: String, toType: String): DFDLCalendar
= {
-
- acceptableFormats.foreach(f => {
- val inFormat = f.getFormat
- inFormat.setCalendar(getNewCalendar)
- try {
- val cal = createCalendar(str, inFormat, f.expectsTimeZone, fncName,
toType)
- // Here we've successfully created a calendar using the expected format
- // denoted by 'inFormat'. Return the calendar.
- return cal
- } catch {
- case e: IllegalArgumentException => /* Format failed, continue trying
to match other formats */
- }
-
- })
- // All acceptable formats failed
- throw new NumberFormatException("Failed to convert \"%s\" to
%s.".format(str, toType))
- }
-}
-
-case object StringToDate extends Converter with XSDateTimeKind with
DateFormatters {
+case object StringToDate extends Converter {
val name = "StringToDate"
- /**
- * A list of acceptable formats as specified by:
http://www.w3.org/TR/NOTE-datetime
- *
- * Order matters here as we are also trying to determine if a time zone was
parsed
- */
- def acceptableFormats = Seq(defaultFormatter.get,
withoutTimezoneFormatter.get)
-
- protected def createCalendar(str: String, inFormat: SimpleDateFormat,
expectsTZ: Boolean, fncName: String, toType: String): DFDLCalendar = {
- Conversion.stringToDFDLDate(str, inFormat, expectsTZ, fncName, toType)
- }
-
override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
val result = a match {
case cal: DFDLDateTime => cal.toDate
case cal: DFDLDate => cal
- case str: String => matchFormat(str, name, "xs:date")
+ case str: String => DFDLDateConversion.fromXMLString(str)
case _ => throw new NumberFormatException("xs:date only accepts String,
Date or DateTime objects.")
}
result
}
}
-case object StringToDateTime extends Converter
- with XSDateTimeKind with DateTimeFormatters {
- val name = "StringToDateTime"
-
- /**
- * A list of acceptable formats as specified by:
http://www.w3.org/TR/NOTE-datetime
- *
- * Order matters here as we are also trying to determine if a time zone was
parsed
- */
- def acceptableFormats = Seq(defaultFormatter.get,
withFractNoTimeZoneFormatter.get,
- withTimeZoneNoFractFormatter.get, noTimeZoneNoFractFormatter.get,
- withTimeZoneFormatter.get, dateOnlyFormatter.get)
- protected def createCalendar(str: String, inFormat: SimpleDateFormat,
- expectsTZ: Boolean, fncName: String, toType: String): DFDLCalendar = {
- Conversion.stringToDFDLDateTime(str, inFormat, expectsTZ, fncName, toType)
- }
+case object StringToDateTime extends Converter {
+ val name = "StringToDateTime"
override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
val result = a match {
case cal: DFDLDateTime => cal
case cal: DFDLDate => cal.toDateTime
- case str: String => matchFormat(str, name, "xs:dateTime")
+ case str: String => DFDLDateTimeConversion.fromXMLString(str)
case _ => throw new NumberFormatException("xs:dateTime only accepts
String, Date or DateTime objects.")
}
result
}
-
}
-case object StringToTime extends Converter with XSDateTimeKind with
TimeFormatters {
+case object StringToTime extends Converter {
val name = "StringToTime"
- /**
- * A list of acceptable formats as specified by:
http://www.w3.org/TR/NOTE-datetime
- *
- * Order matters here as we are also trying to determine if a time zone was
parsed
- */
- def acceptableFormats = Seq(defaultFormatter.get, noTimeZoneFormatter.get,
- withTimeZoneNoFractFormatter.get, noTimeZoneNoFractFormatter.get)
-
- protected def createCalendar(str: String, inFormat: SimpleDateFormat,
- expectsTZ: Boolean, fncName: String, toType: String): DFDLCalendar = {
- Conversion.stringToDFDLTime(str, inFormat, expectsTZ, fncName, toType)
- }
-
override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
val result = a match {
case cal: DFDLDateTime => cal.toTime
case cal: DFDLTime => cal
- case str: String => matchFormat(str, name, "xs:time")
+ case str: String => DFDLTimeConversion.fromXMLString(str)
case _ => throw new NumberFormatException("xs:time only accepts String,
DateTime or Time objects.")
}
result
}
-
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
index f08dd34f1..11133cc0f 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
@@ -17,11 +17,12 @@
package org.apache.daffodil.dpath
-import org.apache.daffodil.exceptions._
-import org.apache.daffodil.dsom._
import org.apache.daffodil.calendar.DFDLCalendar
-import org.apache.daffodil.util.Numbers._
+import org.apache.daffodil.calendar.DFDLCalendarConversion
import org.apache.daffodil.cookers.EntityReplacer
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.exceptions._
+import org.apache.daffodil.util.Numbers._
case class DFDLCheckConstraints(recipe: CompiledDPath) extends
RecipeOpWithSubRecipes(recipe) {
override def run(dstate: DState) {
@@ -114,10 +115,8 @@ case class DFDLTimeZoneFromDFDLCalendar(recipe:
CompiledDPath, argType: NodeInfo
extends FNOneArg(recipe, argType) {
override def computeValue(value: AnyRef, dstate: DState) = {
- val calendar = value.asInstanceOf[DFDLCalendar]
-
- val res = if (calendar.hasTimeZone) { calendar.getTimeZoneString } else {
"" }
- res
+ val dfdlcal = value.asInstanceOf[DFDLCalendar]
+ DFDLCalendarConversion.timeZonePartToXMLString(dfdlcal)
}
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DateTimeConversions.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DateTimeConversions.scala
deleted file mode 100644
index 2f5bd4ea0..000000000
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DateTimeConversions.scala
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.daffodil.dpath
-
-import com.ibm.icu.text.SimpleDateFormat
-
-class DFDLDateFormat(format: SimpleDateFormat, expectsTZ: Boolean = false) {
- def getFormat = format
- def expectsTimeZone = expectsTZ
-}
-
-trait DateFormatters {
- @transient lazy val defaultFormatter = new ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-ddxxxxx"),
true)
- format
- }
- }
- @transient lazy val withoutTimezoneFormatter = new
ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-dd"))
- format
- }
- }
-}
-
-trait TimeFormatters {
- @transient lazy val defaultFormatter = new ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new
SimpleDateFormat("HH:mm:ss.SSSSSSxxxxx"), true)
- format
- }
- }
-
- @transient lazy val noTimeZoneFormatter = new ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new SimpleDateFormat("HH:mm:ss.SSSSSS"))
- format
- }
- }
-
- @transient lazy val noTimeZoneNoFractFormatter = new
ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new SimpleDateFormat("HH:mm:ss"))
- format
- }
- }
-
- @transient lazy val withTimeZoneNoFractFormatter = new
ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new SimpleDateFormat("HH:mm:ssxxxxx"),
true)
- format
- }
- }
-}
-
-trait DateTimeFormatters {
- @transient lazy val defaultFormatter = new ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx"), true)
- format
- }
- }
-
- @transient lazy val withFractNoTimeZoneFormatter = new
ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSS"))
- format
- }
- }
-
- @transient lazy val withTimeZoneNoFractFormatter = new
ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ssxxxxx"), true)
- format
- }
- }
-
- @transient lazy val noTimeZoneNoFractFormatter = new
ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ss"))
- format
- }
- }
-
- @transient lazy val withTimeZoneFormatter = new ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-ddxxxxx"),
true)
- format
- }
- }
-
- @transient lazy val dateOnlyFormatter = new ThreadLocal[DFDLDateFormat] {
- override def initialValue = {
- val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-dd"))
- format
- }
- }
-}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
index cdedb87ee..20d195376 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
@@ -242,7 +242,7 @@ case class FNDateTime(recipes: List[CompiledDPath]) extends
FNTwoArgs(recipes) {
private def calendarToDFDLDateTime(calendar: Calendar, hasTZ: Boolean,
dstate: DState, fncName: String, toType: String): DFDLCalendar = {
try {
- val cal = new DFDLDateTime(calendar, hasTZ)
+ val cal = DFDLDateTime(calendar, hasTZ)
return cal
} catch {
case ex: java.lang.IllegalArgumentException =>
@@ -253,8 +253,8 @@ case class FNDateTime(recipes: List[CompiledDPath]) extends
FNTwoArgs(recipes) {
val dateCalendar = arg1.asInstanceOf[DFDLCalendar]
val timeCalendar = arg2.asInstanceOf[DFDLCalendar]
- val dateCal = dateCalendar.getCalendar
- val timeCal = timeCalendar.getCalendar
+ val dateCal = dateCalendar.calendar
+ val timeCal = timeCalendar.calendar
val year = dateCal.get(Calendar.YEAR)
val day = dateCal.get(Calendar.DAY_OF_MONTH)
@@ -748,7 +748,7 @@ abstract class FNFromDateTime(recipe: CompiledDPath,
argType: NodeInfo.Kind)
with FNFromDateTimeKind {
override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
a match {
- case dt: DFDLDateTime => asAnyRef(dt.getField(field))
+ case dt: DFDLDateTime => asAnyRef(dt.calendar.get(field))
case _ => throw new NumberFormatException("fn:" + fieldName +
"-from-dateTime only accepts xs:dateTime.")
}
}
@@ -759,7 +759,7 @@ abstract class FNFromDate(recipe: CompiledDPath, argType:
NodeInfo.Kind)
with FNFromDateTimeKind {
override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
a match {
- case d: DFDLDate => asAnyRef(d.getField(field))
+ case d: DFDLDate => asAnyRef(d.calendar.get(field))
case _ => throw new NumberFormatException("fn:" + fieldName +
"-from-date only accepts xs:date.")
}
}
@@ -770,7 +770,7 @@ abstract class FNFromTime(recipe: CompiledDPath, argType:
NodeInfo.Kind)
with FNFromDateTimeKind {
override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
a match {
- case t: DFDLTime => asAnyRef(t.getField(field))
+ case t: DFDLTime => asAnyRef(t.calendar.get(field))
case _ => throw new NumberFormatException("fn:" + fieldName +
"-from-time only accepts xs:time.")
}
}
@@ -826,8 +826,8 @@ case class FNSecondsFromDateTime(recipe: CompiledDPath,
argType: NodeInfo.Kind)
//
val res: JNumber = a match {
case dt: DFDLDateTime => {
- val seconds = dt.getField(Calendar.SECOND)
- val frac = dt.getField(Calendar.MILLISECOND)
+ val seconds = dt.calendar.get(Calendar.SECOND)
+ val frac = dt.calendar.get(Calendar.MILLISECOND)
if (frac == 0) { seconds }
else {
val d = seconds + (frac / 1000.0)
@@ -878,8 +878,8 @@ case class FNSecondsFromTime(recipe: CompiledDPath,
argType: NodeInfo.Kind)
//
val res: JNumber = a match {
case dt: DFDLTime => {
- val seconds = dt.getField(Calendar.SECOND)
- val frac = dt.getField(Calendar.MILLISECOND)
+ val seconds = dt.calendar.get(Calendar.SECOND)
+ val frac = dt.calendar.get(Calendar.MILLISECOND)
if (frac == 0) { seconds }
else {
val d = seconds + (frac / 1000.0)
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala
index 047f9bae2..64d7f7efd 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala
@@ -527,27 +527,15 @@ object NodeInfo extends Enum {
case object Date extends PrimTypeNode(AnyDateTime, Nil) with DateKind {
type Kind = DateKind
override def fromXMLString(s: String): AnyRef = {
- StringToDate.computeValue(s, null)
- //val juDate =
TextCalendarConstants.tlDateNoTZInfosetFormatter.get.parse(s)
- //val icuCal = dateToCalendar(juDate)
- //DFDLDate(icuCal, false)
+ DFDLDateConversion.fromXMLString(s)
}
-
- /*
- private def dateToCalendar(date: java.util.Date): Calendar = {
- val cal = Calendar.getInstance()
- cal.setTime(date)
- cal
- }
-*/
-
}
protected sealed trait DateTimeKind extends AnyDateTimeKind
case object DateTime extends PrimTypeNode(AnyDateTime, Nil) with
DateTimeKind {
type Kind = DateTimeKind
override def fromXMLString(s: String): AnyRef = {
- StringToDateTime.computeValue(s, null)
+ DFDLDateTimeConversion.fromXMLString(s)
}
}
@@ -555,7 +543,7 @@ object NodeInfo extends Enum {
case object Time extends PrimTypeNode(AnyDateTime, Nil) with TimeKind {
type Kind = TimeKind
override def fromXMLString(s: String): AnyRef = {
- StringToTime.computeValue(s, null)
+ DFDLTimeConversion.fromXMLString(s)
}
}
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
index 19e860059..5825ca845 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
@@ -144,9 +144,9 @@ case class ConvertTextCalendarParser(
}
val newCal = xsdType.toLowerCase() match {
- case "time" => new DFDLTime(cal, hasTZ)
- case "date" => new DFDLDate(cal, hasTZ)
- case "datetime" => new DFDLDateTime(cal, hasTZ)
+ case "time" => DFDLTime(cal, hasTZ)
+ case "date" => DFDLDate(cal, hasTZ)
+ case "datetime" => DFDLDateTime(cal, hasTZ)
case _ => Assert.impossibleCase
}
@@ -155,37 +155,6 @@ case class ConvertTextCalendarParser(
}
}
-object TextCalendarConstants {
- final val maxFractionalSeconds = 9
-
- // before being used, setCalendar must be called on the SimpleDateFormat
- //
- // The reason for the various NoTZ and NoFractSec variations are because
- // we need to keep track of whether the data had timezone or millisecond
- // information initially, otherwise we end up assuming a timezone, such
- // as UTC, and .000000 milliseconds when the data did not specify this
- // information.
- final val tlDateTimeNoTZInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ss.SSSSSS")
- final val tlDateTimeInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx")
- final val tlDateTimeNoTZNoFractSecInfosetFormatter:
ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ss")
- final val tlDateTimeNoFractSecInfosetFormatter:
ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ssxxxxx")
- final val tlDateNoTZInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("uuuu-MM-dd")
- final val tlDateInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("uuuu-MM-ddxxxxx")
- final val tlTimeNoTZInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("HH:mm:ss.SSSSSS")
- final val tlTimeInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("HH:mm:ss.SSSSSSxxxxx")
- final val tlTimeNoTZNoFractSecInfosetFormatter:
ThreadLocal[SimpleDateFormat] = createTLInfosetFormatter("HH:mm:ss")
- final val tlTimeNoFractSecInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("HH:mm:ssxxxxx")
- final val tlTzInfosetFormatter: ThreadLocal[SimpleDateFormat] =
createTLInfosetFormatter("xxx") // -08:00 The ISO8601 extended format with
hours and minutes fields.
-
- private def createTLInfosetFormatter(pattern: String) = new
ThreadLocal[SimpleDateFormat] {
- override def initialValue = {
- val formatter = new SimpleDateFormat(pattern)
- formatter.setLenient(true)
- formatter
- }
- }
-}
-
case class ConvertBinaryCalendarSecMilliParser(
override val context: ElementRuntimeData,
hasTZ: Boolean,
@@ -229,7 +198,7 @@ case class ConvertBinaryCalendarSecMilliParser(
}
}
- val newCal = new DFDLDateTime(cal, hasTZ)
+ val newCal = DFDLDateTime(cal, hasTZ)
start.simpleElement.overwriteDataValue(newCal)
}
}
diff --git
a/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
b/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
index da372e7bb..01be886c9 100644
---
a/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
+++
b/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
@@ -25,7 +25,7 @@ import com.ibm.icu.util.Calendar
import com.ibm.icu.text.DecimalFormat
import com.ibm.icu.text.DecimalFormatSymbols
import org.junit.Test
-import org.apache.daffodil.processors.parsers.TextCalendarConstants
+import org.apache.daffodil.calendar.TextCalendarConstants
class TestICU {
diff --git
a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
index 5c3f9cdc9..0ccbdef52 100644
--- a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
+++ b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
@@ -1267,40 +1267,10 @@ case class UnparserTestCase(ptc: NodeSeq, parentArg:
DFDLTestSuite)
object VerifyTestCase {
def verifyParserTestData(actual: Node, infoset: Infoset, implString:
Option[String]) {
- val actualForCompare = XMLUtils.removeAttributes(actual)
-
- // For debug
- // scala.xml.XML.save("/tmp/actual.out.xml", actual, "utf-8")
-
- //
- // Would be great to validate the actuals against the DFDL schema, used as
- // an XML schema on the returned infoset XML.
- // Getting this to work is a bigger issue. What with stripping of
attributes
- // and that our internal Daffodil XML Catalog has a special treatment of
the
- // mapping of the XML Schema URI.
- // etc.
- //
- // TODO: Fix so we can validate here.
- //
-
- // Something about the way XML is constructed is different between our
infoset
- // results and the ones created by scala directly parsing the TDML test
files.
- //
- // This has something to do with values being lists of text nodes and
entities
- // and not just simple strings. I.e., if you write: <foo>a年</foo>,
that's not
- // an element with a string as its value. It's an element with several
text nodes as
- // its values.
- //
- // so we run the expected stuff through the same conditioners as the actual
- // data so that they are properly comparable.
- //
-
val expected = infoset.contents
- val expectedForCompare =
XMLUtils.removeAttributes(XMLUtils.convertPCDataToText(expected))
-
try {
- XMLUtils.compareAndReport(expectedForCompare, actualForCompare)
+ XMLUtils.compareAndReport(expected, actual)
} catch {
case e: Exception =>
throw TDMLException(e, implString)
diff --git
a/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
b/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
index ab8952093..9c0b18a64 100644
---
a/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
+++
b/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
@@ -521,4 +521,172 @@ f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
lazy val r = new DFDLTestSuite(Misc.getRequiredResource(t0))
r.runOneTest("tdmlNamespaces1")
}
+
+
+ val testHexBinarySchema = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+ <dfdl:format ref="tns:GeneralFormat"/>,
+ <xs:element name="data" type="xs:hexBinary" dfdl:lengthKind="explicit"
dfdl:length="4"/>)
+
+ @Test def testTDMLHexBinaryTypeAwareSuccess_01() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLHexBinaryTypeAwareSuccess"
+ root="data">
+ <ts:document>
+ <ts:documentPart
type="byte">A1B2C3D4</ts:documentPart>
+ </ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example } xmlns:xsi={ xsi }
xmlns:xs={ xsd }
+ xsi:type="xs:hexBinary">a1b2c3d4</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ ts.runOneTest("testTDMLHexBinaryTypeAwareSuccess",
Some(testHexBinarySchema))
+ }
+
+ @Test def testTDMLHexBinaryTypeAwareSuccess_02() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLHexBinaryTypeAwareSuccess"
+ root="data">
+ <ts:document>
+ <ts:documentPart
type="byte">A1B2C3D4</ts:documentPart>
+ </ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example } xmlns:xsi={ xsi }
xmlns:xs={ xsd }
+ xsi:type="xs:hexBinary">A1B2C3D4</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ ts.runOneTest("testTDMLHexBinaryTypeAwareSuccess",
Some(testHexBinarySchema))
+ }
+
+ @Test def testTDMLHexBinaryTypeAwareFailure() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLHexBinaryTypeAwareFailure"
+ root="data">
+ <ts:document>
+ <ts:documentPart
type="byte">A1B2C3D4</ts:documentPart>
+ </ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example }>a1b2c3d4</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ val e = intercept[Exception] {
+ ts.runOneTest("testTDMLHexBinaryTypeAwareFailure",
Some(testHexBinarySchema))
+ }
+ val msg = e.getMessage()
+ assertTrue(msg.contains("Comparison failed"))
+ assertTrue(msg.contains("a1b2c3d4"))
+ assertTrue(msg.contains("A1B2C3D4"))
+ }
+
+
+
+ val testDateTimeSchema = SchemaUtils.dfdlTestSchema(
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+ <dfdl:format ref="tns:GeneralFormat"/>,
+ <xs:element name="data" type="xs:dateTime" dfdl:lengthKind="explicit"
dfdl:length="32"
+ dfdl:calendarPatternKind="explicit"
+ dfdl:calendarPattern="uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx" />)
+
+ @Test def testTDMLDateTimeTypeAwareSuccess_01() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLDateTimeTypeAwareSuccess"
+ root="data">
+
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example } xmlns:xsi={ xsi }
xmlns:xs={ xsd }
+
xsi:type="xs:dateTime">1995-03-24T01:30:00Z</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+ }
+
+ @Test def testTDMLDateTimeTypeAwareSuccess_02() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLDateTimeTypeAwareSuccess"
+ root="data">
+
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example } xmlns:xsi={ xsi }
xmlns:xs={ xsd }
+
xsi:type="xs:dateTime">1995-03-24T01:30:00+00:00</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+ }
+
+ @Test def testTDMLDateTimeTypeAwareSuccess_03() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLDateTimeTypeAwareSuccess"
+ root="data">
+
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example } xmlns:xsi={ xsi }
xmlns:xs={ xsd }
+
xsi:type="xs:dateTime">1995-03-24T01:30:00.000+00:00</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+ }
+
+ @Test def testTDMLDateTimeTypeAwareSuccess_04() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLDateTimeTypeAwareSuccess"
+ root="data">
+
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example } xmlns:xsi={ xsi }
xmlns:xs={ xsd }
+
xsi:type="xs:dateTime">1995-03-24T01:30:00.000000Z</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+ }
+
+ @Test def testTDMLDateTimeTypeAwareFailure() {
+ val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+ <ts:parserTestCase ID="some identifier"
name="testTDMLDateTimeTypeAwareFailure"
+ root="data">
+
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+ <ts:infoset>
+ <ts:dfdlInfoset>
+ <data xmlns={ example }>1995-03-24T01:30:00Z</data>
+ </ts:dfdlInfoset>
+ </ts:infoset>
+ </ts:parserTestCase>
+ </ts:testSuite>
+ val ts = new DFDLTestSuite(testSuite)
+ val e = intercept[Exception] {
+ ts.runOneTest("testTDMLDateTimeTypeAwareFailure",
Some(testDateTimeSchema))
+ }
+ val msg = e.getMessage()
+ assertTrue(msg.contains("Comparison failed"))
+ assertTrue(msg.contains("1995-03-24T01:30:00Z"))
+ assertTrue(msg.contains("1995-03-24T01:30:00+00:00"))
+ }
+
}
diff --git
a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml
b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml
index eb72ec775..dc6ecb7b4 100644
---
a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml
+++
b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml
@@ -276,8 +276,7 @@
<tdml:parserTestCase name="simple_type_properties_text_calendar_13_02"
root="calendar_group"
roundTrip="twoPass"
model="./fvt/ext/dpa/dpacaltxt_43.dfdl.xsd"
- description="Section 13.10 Properties for calendar text -
implicit patterns for date, time, and dateTime "
- implementations="daffodil">
+ description="Section 13.10 Properties for calendar text -
implicit patterns for date, time, and dateTime ">
<tdml:document>2010-12-30*04:05:06+01:00*2010-12-30T04:05:06@@</tdml:document>
<tdml:infoset>
<tdml:dfdlInfoset>
@@ -289,27 +288,6 @@
</tdml:dfdlInfoset>
</tdml:infoset>
</tdml:parserTestCase>
-
- <!-- Until DAFFODIL-2026 (type-dependent comparison for TDML tests) is
implemented, we need
- some tests to be split into IBM tests, and Daffodil tests since different
equivalent infosets are
- allowed. -->
- <tdml:parserTestCase name="simple_type_properties_text_calendar_13_02_ibm"
root="calendar_group"
- roundTrip="twoPass"
- model="./fvt/ext/dpa/dpacaltxt_43.dfdl.xsd"
- description="Section 13.10 Properties for calendar text - implicit
patterns for date, time, and dateTime "
- implementations="ibm">
-
<tdml:document>2010-12-30*04:05:06+01:00*2010-12-30T04:05:06@@</tdml:document>
- <tdml:infoset>
- <tdml:dfdlInfoset>
- <calendar_group>
- <date1 xsi:type="xs:date">2010-12-30Z</date1>
- <time1 xsi:type="xs:time">04:05:06.000+01:00</time1>
- <datetime1
xsi:type="xs:dateTime">2010-12-30T04:05:06.000Z</datetime1>
- </calendar_group>
- </tdml:dfdlInfoset>
- </tdml:infoset>
- </tdml:parserTestCase>
-
<tdml:parserTestCase name="simple_type_properties_text_calendar_13_03"
root="myDateTime"
roundTrip="twoPass"
diff --git
a/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala
b/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala
index 09263c8f5..4e398602b 100644
--- a/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala
+++ b/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala
@@ -47,8 +47,6 @@ class IBMTestsThatPass {
// Doesn't work for a user, possible locale issue (DAFFODIL-1945)
// @Test def test_simple_type_properties_text_calendar_13_01() {
runner2.runOneTest("simple_type_properties_text_calendar_13_01") }
@Test def test_simple_type_properties_text_calendar_13_02() {
runner2.runOneTest("simple_type_properties_text_calendar_13_02") }
- // Equivalent IBM test needed until DAFFODIL-2026 is implemented
- @Test def test_simple_type_properties_text_calendar_13_02_ibm() {
runner2.runOneTest("simple_type_properties_text_calendar_13_02_ibm") }
@Test def test_simple_type_properties_text_calendar_13_03() {
runner2.runOneTest("simple_type_properties_text_calendar_13_03") }
@Test def test_simple_type_properties_text_calendar_13_04() {
runner2.runOneTest("simple_type_properties_text_calendar_13_04") }
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
index f5568f05e..f2b9dedda 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
@@ -329,8 +329,7 @@
<first>bat</first>
<second>cz</second>
<third>3 4</third>
- <fourth>
- </fourth>
+ <fourth><![CDATA[ ]]></fourth>
<fifth>AAAAA</fifth>
</list_01>
</tdml:dfdlInfoset>
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services